aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir_def/src/nameres/raw.rs32
-rw-r--r--crates/ra_hir_def/src/nameres/tests.rs9
-rw-r--r--crates/ra_hir_ty/src/lib.rs2
-rw-r--r--crates/ra_hir_ty/src/tests.rs52
-rw-r--r--crates/ra_hir_ty/src/tests/regression.rs41
-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
-rw-r--r--crates/ra_syntax/src/ptr.rs4
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs6
-rw-r--r--crates/rust-analyzer/src/config.rs15
-rw-r--r--crates/rust-analyzer/src/conv.rs12
-rw-r--r--crates/rust-analyzer/src/world.rs2
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs11
26 files changed, 698 insertions, 153 deletions
diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs
index afd538e4a..39b011ad7 100644
--- a/crates/ra_hir_def/src/nameres/raw.rs
+++ b/crates/ra_hir_def/src/nameres/raw.rs
@@ -266,8 +266,8 @@ impl RawItemsCollector {
266 self.add_macro(current_module, it); 266 self.add_macro(current_module, it);
267 return; 267 return;
268 } 268 }
269 ast::ModuleItem::ExternBlock(_) => { 269 ast::ModuleItem::ExternBlock(it) => {
270 // FIXME: add extern block 270 self.add_extern_block(current_module, it);
271 return; 271 return;
272 } 272 }
273 }; 273 };
@@ -278,6 +278,34 @@ impl RawItemsCollector {
278 } 278 }
279 } 279 }
280 280
281 fn add_extern_block(
282 &mut self,
283 current_module: Option<Idx<ModuleData>>,
284 block: ast::ExternBlock,
285 ) {
286 if let Some(items) = block.extern_item_list() {
287 for item in items.extern_items() {
288 let attrs = self.parse_attrs(&item);
289 let visibility =
290 RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene);
291 let (kind, name) = match item {
292 ast::ExternItem::FnDef(it) => {
293 (DefKind::Function(self.source_ast_id_map.ast_id(&it)), it.name())
294 }
295 ast::ExternItem::StaticDef(it) => {
296 (DefKind::Static(self.source_ast_id_map.ast_id(&it)), it.name())
297 }
298 };
299
300 if let Some(name) = name {
301 let name = name.as_name();
302 let def = self.raw_items.defs.alloc(DefData { name, kind, visibility });
303 self.push_item(current_module, attrs, RawItemKind::Def(def));
304 }
305 }
306 }
307 }
308
281 fn add_module(&mut self, current_module: Option<Idx<ModuleData>>, module: ast::Module) { 309 fn add_module(&mut self, current_module: Option<Idx<ModuleData>>, module: ast::Module) {
282 let name = match module.name() { 310 let name = match module.name() {
283 Some(it) => it.as_name(), 311 Some(it) => it.as_name(),
diff --git a/crates/ra_hir_def/src/nameres/tests.rs b/crates/ra_hir_def/src/nameres/tests.rs
index 949ca7595..83120fa36 100644
--- a/crates/ra_hir_def/src/nameres/tests.rs
+++ b/crates/ra_hir_def/src/nameres/tests.rs
@@ -25,7 +25,7 @@ fn compute_crate_def_map(fixture: &str) -> Arc<CrateDefMap> {
25#[test] 25#[test]
26fn crate_def_map_smoke_test() { 26fn crate_def_map_smoke_test() {
27 let map = def_map( 27 let map = def_map(
28 " 28 r"
29 //- /lib.rs 29 //- /lib.rs
30 mod foo; 30 mod foo;
31 struct S; 31 struct S;
@@ -45,6 +45,11 @@ fn crate_def_map_smoke_test() {
45 } 45 }
46 46
47 enum E { V } 47 enum E { V }
48
49 extern {
50 static EXT: u8;
51 fn ext();
52 }
48 ", 53 ",
49 ); 54 );
50 assert_snapshot!(map, @r###" 55 assert_snapshot!(map, @r###"
@@ -61,7 +66,9 @@ fn crate_def_map_smoke_test() {
61 ⋮crate::foo::bar 66 ⋮crate::foo::bar
62 ⋮Baz: t v 67 ⋮Baz: t v
63 ⋮E: t 68 ⋮E: t
69 ⋮EXT: v
64 ⋮U: t v 70 ⋮U: t v
71 ⋮ext: v
65 "###) 72 "###)
66} 73}
67 74
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index a4b8d6683..279c06d65 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -863,7 +863,7 @@ pub trait TypeWalk {
863 &mut |ty, binders| { 863 &mut |ty, binders| {
864 if let &mut Ty::Bound(bound) = ty { 864 if let &mut Ty::Bound(bound) = ty {
865 if bound.debruijn >= binders { 865 if bound.debruijn >= binders {
866 *ty = substs.0[bound.index].clone(); 866 *ty = substs.0[bound.index].clone().shift_bound_vars(binders);
867 } 867 }
868 } 868 }
869 }, 869 },
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index 81fc0f63a..846005baa 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -18,16 +18,19 @@ use hir_def::{
18 nameres::CrateDefMap, 18 nameres::CrateDefMap,
19 AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, 19 AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId,
20}; 20};
21use hir_expand::InFile; 21use hir_expand::{db::AstDatabase, InFile};
22use insta::assert_snapshot; 22use insta::assert_snapshot;
23use ra_db::{fixture::WithFixture, salsa::Database, FilePosition, SourceDatabase}; 23use ra_db::{fixture::WithFixture, salsa::Database, FilePosition, SourceDatabase};
24use ra_syntax::{ 24use ra_syntax::{
25 algo, 25 algo,
26 ast::{self, AstNode}, 26 ast::{self, AstNode},
27 SyntaxNode,
27}; 28};
28use stdx::format_to; 29use stdx::format_to;
29 30
30use crate::{db::HirDatabase, display::HirDisplay, test_db::TestDB, InferenceResult}; 31use crate::{
32 db::HirDatabase, display::HirDisplay, infer::TypeMismatch, test_db::TestDB, InferenceResult, Ty,
33};
31 34
32// These tests compare the inference results for all expressions in a file 35// These tests compare the inference results for all expressions in a file
33// against snapshots of the expected results using insta. Use cargo-insta to 36// against snapshots of the expected results using insta. Use cargo-insta to
@@ -67,13 +70,19 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
67 70
68 let mut infer_def = |inference_result: Arc<InferenceResult>, 71 let mut infer_def = |inference_result: Arc<InferenceResult>,
69 body_source_map: Arc<BodySourceMap>| { 72 body_source_map: Arc<BodySourceMap>| {
70 let mut types = Vec::new(); 73 let mut types: Vec<(InFile<SyntaxNode>, &Ty)> = Vec::new();
71 let mut mismatches = Vec::new(); 74 let mut mismatches: Vec<(InFile<SyntaxNode>, &TypeMismatch)> = Vec::new();
72 75
73 for (pat, ty) in inference_result.type_of_pat.iter() { 76 for (pat, ty) in inference_result.type_of_pat.iter() {
74 let syntax_ptr = match body_source_map.pat_syntax(pat) { 77 let syntax_ptr = match body_source_map.pat_syntax(pat) {
75 Ok(sp) => { 78 Ok(sp) => {
76 sp.map(|ast| ast.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr())) 79 let root = db.parse_or_expand(sp.file_id).unwrap();
80 sp.map(|ptr| {
81 ptr.either(
82 |it| it.to_node(&root).syntax().clone(),
83 |it| it.to_node(&root).syntax().clone(),
84 )
85 })
77 } 86 }
78 Err(SyntheticSyntax) => continue, 87 Err(SyntheticSyntax) => continue,
79 }; 88 };
@@ -81,29 +90,31 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
81 } 90 }
82 91
83 for (expr, ty) in inference_result.type_of_expr.iter() { 92 for (expr, ty) in inference_result.type_of_expr.iter() {
84 let syntax_ptr = match body_source_map.expr_syntax(expr) { 93 let node = match body_source_map.expr_syntax(expr) {
85 Ok(sp) => sp.map(|ast| ast.syntax_node_ptr()), 94 Ok(sp) => {
95 let root = db.parse_or_expand(sp.file_id).unwrap();
96 sp.map(|ptr| ptr.to_node(&root).syntax().clone())
97 }
86 Err(SyntheticSyntax) => continue, 98 Err(SyntheticSyntax) => continue,
87 }; 99 };
88 types.push((syntax_ptr.clone(), ty)); 100 types.push((node.clone(), ty));
89 if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr) { 101 if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr) {
90 mismatches.push((syntax_ptr, mismatch)); 102 mismatches.push((node, mismatch));
91 } 103 }
92 } 104 }
93 105
94 // sort ranges for consistency 106 // sort ranges for consistency
95 types.sort_by_key(|(src_ptr, _)| { 107 types.sort_by_key(|(node, _)| {
96 (src_ptr.value.range().start(), src_ptr.value.range().end()) 108 let range = node.value.text_range();
109 (range.start(), range.end())
97 }); 110 });
98 for (src_ptr, ty) in &types { 111 for (node, ty) in &types {
99 let node = src_ptr.value.to_node(&src_ptr.file_syntax(&db)); 112 let (range, text) = if let Some(self_param) = ast::SelfParam::cast(node.value.clone()) {
100
101 let (range, text) = if let Some(self_param) = ast::SelfParam::cast(node.clone()) {
102 (self_param.self_token().unwrap().text_range(), "self".to_string()) 113 (self_param.self_token().unwrap().text_range(), "self".to_string())
103 } else { 114 } else {
104 (src_ptr.value.range(), node.text().to_string().replace("\n", " ")) 115 (node.value.text_range(), node.value.text().to_string().replace("\n", " "))
105 }; 116 };
106 let macro_prefix = if src_ptr.file_id != file_id.into() { "!" } else { "" }; 117 let macro_prefix = if node.file_id != file_id.into() { "!" } else { "" };
107 format_to!( 118 format_to!(
108 buf, 119 buf,
109 "{}{} '{}': {}\n", 120 "{}{} '{}': {}\n",
@@ -114,11 +125,12 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
114 ); 125 );
115 } 126 }
116 if include_mismatches { 127 if include_mismatches {
117 mismatches.sort_by_key(|(src_ptr, _)| { 128 mismatches.sort_by_key(|(node, _)| {
118 (src_ptr.value.range().start(), src_ptr.value.range().end()) 129 let range = node.value.text_range();
130 (range.start(), range.end())
119 }); 131 });
120 for (src_ptr, mismatch) in &mismatches { 132 for (src_ptr, mismatch) in &mismatches {
121 let range = src_ptr.value.range(); 133 let range = src_ptr.value.text_range();
122 let macro_prefix = if src_ptr.file_id != file_id.into() { "!" } else { "" }; 134 let macro_prefix = if src_ptr.file_id != file_id.into() { "!" } else { "" };
123 format_to!( 135 format_to!(
124 buf, 136 buf,
diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs
index 61284d672..61a6801fc 100644
--- a/crates/ra_hir_ty/src/tests/regression.rs
+++ b/crates/ra_hir_ty/src/tests/regression.rs
@@ -533,3 +533,44 @@ fn foo(b: Bar) {
533 "### 533 "###
534 ); 534 );
535} 535}
536
537#[test]
538fn issue_4053_diesel_where_clauses() {
539 assert_snapshot!(
540 infer(r#"
541trait BoxedDsl<DB> {
542 type Output;
543 fn internal_into_boxed(self) -> Self::Output;
544}
545
546struct SelectStatement<From, Select, Distinct, Where, Order, LimitOffset, GroupBy, Locking> {
547 order: Order,
548}
549
550trait QueryFragment<DB: Backend> {}
551
552trait Into<T> { fn into(self) -> T; }
553
554impl<F, S, D, W, O, LOf, DB> BoxedDsl<DB>
555 for SelectStatement<F, S, D, W, O, LOf, G>
556where
557 O: Into<dyn QueryFragment<DB>>,
558{
559 type Output = XXX;
560
561 fn internal_into_boxed(self) -> Self::Output {
562 self.order.into();
563 }
564}
565"#),
566 @r###"
567 [66; 70) 'self': Self
568 [268; 272) 'self': Self
569 [467; 471) 'self': SelectStatement<F, S, D, W, O, LOf, {unknown}, {unknown}>
570 [489; 523) '{ ... }': ()
571 [499; 503) 'self': SelectStatement<F, S, D, W, O, LOf, {unknown}, {unknown}>
572 [499; 509) 'self.order': O
573 [499; 516) 'self.o...into()': dyn QueryFragment<DB>
574 "###
575 );
576}
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()?;
diff --git a/crates/ra_syntax/src/ptr.rs b/crates/ra_syntax/src/ptr.rs
index 3be648c2a..ecbfffcf4 100644
--- a/crates/ra_syntax/src/ptr.rs
+++ b/crates/ra_syntax/src/ptr.rs
@@ -30,10 +30,6 @@ impl SyntaxNodePtr {
30 .unwrap_or_else(|| panic!("can't resolve local ptr to SyntaxNode: {:?}", self)) 30 .unwrap_or_else(|| panic!("can't resolve local ptr to SyntaxNode: {:?}", self))
31 } 31 }
32 32
33 pub fn range(&self) -> TextRange {
34 self.range
35 }
36
37 pub fn cast<N: AstNode>(self) -> Option<AstPtr<N>> { 33 pub fn cast<N: AstNode>(self) -> Option<AstPtr<N>> {
38 if !N::can_cast(self.kind) { 34 if !N::can_cast(self.kind) {
39 return None; 35 return None;
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index d442cbd63..9fa7dad71 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -162,9 +162,13 @@ pub fn analysis_stats(
162 let (_, sm) = db.body_with_source_map(f_id.into()); 162 let (_, sm) = db.body_with_source_map(f_id.into());
163 let src = sm.expr_syntax(expr_id); 163 let src = sm.expr_syntax(expr_id);
164 if let Ok(src) = src { 164 if let Ok(src) = src {
165 let node = {
166 let root = db.parse_or_expand(src.file_id).unwrap();
167 src.value.to_node(&root)
168 };
165 let original_file = src.file_id.original_file(db); 169 let original_file = src.file_id.original_file(db);
166 let line_index = host.analysis().file_line_index(original_file).unwrap(); 170 let line_index = host.analysis().file_line_index(original_file).unwrap();
167 let text_range = src.value.syntax_node_ptr().range(); 171 let text_range = node.syntax().text_range();
168 let (start, end) = ( 172 let (start, end) = (
169 line_index.line_col(text_range.start()), 173 line_index.line_col(text_range.start()),
170 line_index.line_col(text_range.end()), 174 line_index.line_col(text_range.end()),
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index fa0b1e226..715eddadb 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -7,6 +7,8 @@
7//! configure the server itself, feature flags are passed into analysis, and 7//! configure the server itself, feature flags are passed into analysis, and
8//! tweak things like automatic insertion of `()` in completions. 8//! tweak things like automatic insertion of `()` in completions.
9 9
10use std::{ffi::OsString, path::PathBuf};
11
10use lsp_types::TextDocumentClientCapabilities; 12use lsp_types::TextDocumentClientCapabilities;
11use ra_flycheck::FlycheckConfig; 13use ra_flycheck::FlycheckConfig;
12use ra_ide::{CompletionConfig, InlayHintsConfig}; 14use ra_ide::{CompletionConfig, InlayHintsConfig};
@@ -20,7 +22,7 @@ pub struct Config {
20 pub with_sysroot: bool, 22 pub with_sysroot: bool,
21 pub publish_diagnostics: bool, 23 pub publish_diagnostics: bool,
22 pub lru_capacity: Option<usize>, 24 pub lru_capacity: Option<usize>,
23 pub proc_macro_srv: Option<(String, Vec<String>)>, 25 pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>,
24 pub files: FilesConfig, 26 pub files: FilesConfig,
25 pub notifications: NotificationsConfig, 27 pub notifications: NotificationsConfig,
26 28
@@ -102,6 +104,7 @@ impl Default for Config {
102 enable_postfix_completions: true, 104 enable_postfix_completions: true,
103 add_call_parenthesis: true, 105 add_call_parenthesis: true,
104 add_call_argument_snippets: true, 106 add_call_argument_snippets: true,
107 ..CompletionConfig::default()
105 }, 108 },
106 call_info_full: true, 109 call_info_full: true,
107 } 110 }
@@ -135,7 +138,7 @@ impl Config {
135 match get(value, "/procMacro/enable") { 138 match get(value, "/procMacro/enable") {
136 Some(true) => { 139 Some(true) => {
137 if let Ok(path) = std::env::current_exe() { 140 if let Ok(path) = std::env::current_exe() {
138 self.proc_macro_srv = Some((path.to_string_lossy().to_string(), vec!["proc-macro".to_string()])); 141 self.proc_macro_srv = Some((path, vec!["proc-macro".into()]));
139 } 142 }
140 } 143 }
141 _ => self.proc_macro_srv = None, 144 _ => self.proc_macro_srv = None,
@@ -212,5 +215,13 @@ impl Config {
212 if let Some(value) = caps.folding_range.as_ref().and_then(|it| it.line_folding_only) { 215 if let Some(value) = caps.folding_range.as_ref().and_then(|it| it.line_folding_only) {
213 self.client_caps.line_folding_only = value 216 self.client_caps.line_folding_only = value
214 } 217 }
218 self.completion.allow_snippets(false);
219 if let Some(completion) = &caps.completion {
220 if let Some(completion_item) = &completion.completion_item {
221 if let Some(value) = completion_item.snippet_support {
222 self.completion.allow_snippets(value);
223 }
224 }
225 }
215 } 226 }
216} 227}
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs
index 8d2360cc8..098ee369c 100644
--- a/crates/rust-analyzer/src/conv.rs
+++ b/crates/rust-analyzer/src/conv.rs
@@ -125,7 +125,7 @@ impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem {
125 for atom_edit in self.text_edit().as_atoms() { 125 for atom_edit in self.text_edit().as_atoms() {
126 if self.source_range().is_subrange(&atom_edit.delete) { 126 if self.source_range().is_subrange(&atom_edit.delete) {
127 text_edit = Some(if atom_edit.delete == self.source_range() { 127 text_edit = Some(if atom_edit.delete == self.source_range() {
128 atom_edit.conv_with(ctx) 128 atom_edit.conv_with((ctx.0, ctx.1))
129 } else { 129 } else {
130 assert!(self.source_range().end() == atom_edit.delete.end()); 130 assert!(self.source_range().end() == atom_edit.delete.end());
131 let range1 = 131 let range1 =
@@ -133,12 +133,12 @@ impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem {
133 let range2 = self.source_range(); 133 let range2 = self.source_range();
134 let edit1 = AtomTextEdit::replace(range1, String::new()); 134 let edit1 = AtomTextEdit::replace(range1, String::new());
135 let edit2 = AtomTextEdit::replace(range2, atom_edit.insert.clone()); 135 let edit2 = AtomTextEdit::replace(range2, atom_edit.insert.clone());
136 additional_text_edits.push(edit1.conv_with(ctx)); 136 additional_text_edits.push(edit1.conv_with((ctx.0, ctx.1)));
137 edit2.conv_with(ctx) 137 edit2.conv_with((ctx.0, ctx.1))
138 }) 138 })
139 } else { 139 } else {
140 assert!(self.source_range().intersection(&atom_edit.delete).is_none()); 140 assert!(self.source_range().intersection(&atom_edit.delete).is_none());
141 additional_text_edits.push(atom_edit.conv_with(ctx)); 141 additional_text_edits.push(atom_edit.conv_with((ctx.0, ctx.1)));
142 } 142 }
143 } 143 }
144 let text_edit = text_edit.unwrap(); 144 let text_edit = text_edit.unwrap();
@@ -165,6 +165,10 @@ impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem {
165 ..Default::default() 165 ..Default::default()
166 }; 166 };
167 167
168 if self.score().is_some() {
169 res.preselect = Some(true)
170 }
171
168 if self.deprecated() { 172 if self.deprecated() {
169 res.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated]) 173 res.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated])
170 } 174 }
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs
index 7c0bb42aa..34941931b 100644
--- a/crates/rust-analyzer/src/world.rs
+++ b/crates/rust-analyzer/src/world.rs
@@ -153,7 +153,7 @@ impl WorldState {
153 Err(err) => { 153 Err(err) => {
154 log::error!( 154 log::error!(
155 "Failed to run ra_proc_macro_srv from path {}, error: {:?}", 155 "Failed to run ra_proc_macro_srv from path {}, error: {:?}",
156 path, 156 path.display(),
157 err 157 err
158 ); 158 );
159 ProcMacroClient::dummy() 159 ProcMacroClient::dummy()
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index 1dd2676b6..b31533e5e 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -1,6 +1,6 @@
1mod support; 1mod support;
2 2
3use std::{collections::HashMap, time::Instant}; 3use std::{collections::HashMap, path::PathBuf, time::Instant};
4 4
5use lsp_types::{ 5use lsp_types::{
6 CodeActionContext, DidOpenTextDocumentParams, DocumentFormattingParams, FormattingOptions, 6 CodeActionContext, DidOpenTextDocumentParams, DocumentFormattingParams, FormattingOptions,
@@ -692,15 +692,10 @@ pub fn foo(_input: TokenStream) -> TokenStream {
692"###, 692"###,
693 ) 693 )
694 .with_config(|config| { 694 .with_config(|config| {
695 // FIXME: Use env!("CARGO_BIN_EXE_ra-analyzer") instead after 695 let macro_srv_path = PathBuf::from(env!("CARGO_BIN_EXE_rust-analyzer"));
696 // https://github.com/rust-lang/cargo/pull/7697 landed
697 let macro_srv_path = std::path::Path::new(std::env!("CARGO_MANIFEST_DIR"))
698 .join("../../target/debug/rust-analyzer")
699 .to_string_lossy()
700 .to_string();
701 696
702 config.cargo.load_out_dirs_from_check = true; 697 config.cargo.load_out_dirs_from_check = true;
703 config.proc_macro_srv = Some((macro_srv_path, vec!["proc-macro".to_string()])); 698 config.proc_macro_srv = Some((macro_srv_path, vec!["proc-macro".into()]));
704 }) 699 })
705 .root("foo") 700 .root("foo")
706 .root("bar") 701 .root("bar")