diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-10-22 21:46:24 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-10-22 21:46:24 +0100 |
commit | 8b3c851dd37f39f79e7e8807378f45fdde7f6411 (patch) | |
tree | d6c20cd94e6291d6e4c3b8f58a17817d61463d79 /crates/completion | |
parent | edf46a13a6a28093985d2d934ef97570947b9494 (diff) | |
parent | 3dbbcfca67ed09322227f2190b5364754a29a216 (diff) |
Merge #6098
6098: Insert ref for completions r=adamrk a=adamrk
Follow up to https://github.com/rust-analyzer/rust-analyzer/pull/5846. When we have a local in scope which needs a ref or mutable ref to match the name and type of the active in the completion context then a new completion item with `&` or `&mut ` is inserted. E.g.
```rust
fn foo(arg: &i32){};
fn main() {
let arg = 1_i32;
foo(a<|>)
}
```
now offers `&arg` as a completion option with the highest score.
Co-authored-by: adamrk <[email protected]>
Diffstat (limited to 'crates/completion')
-rw-r--r-- | crates/completion/src/completion_context.rs | 13 | ||||
-rw-r--r-- | crates/completion/src/completion_item.rs | 21 | ||||
-rw-r--r-- | crates/completion/src/presentation.rs | 51 |
3 files changed, 66 insertions, 19 deletions
diff --git a/crates/completion/src/completion_context.rs b/crates/completion/src/completion_context.rs index dc4e136c6..e4f86d0e0 100644 --- a/crates/completion/src/completion_context.rs +++ b/crates/completion/src/completion_context.rs | |||
@@ -246,6 +246,19 @@ impl<'a> CompletionContext<'a> { | |||
246 | } | 246 | } |
247 | } | 247 | } |
248 | 248 | ||
249 | pub(crate) fn active_name_and_type(&self) -> Option<(String, Type)> { | ||
250 | if let Some(record_field) = &self.record_field_syntax { | ||
251 | mark::hit!(record_field_type_match); | ||
252 | let (struct_field, _local) = self.sema.resolve_record_field(record_field)?; | ||
253 | Some((struct_field.name(self.db).to_string(), struct_field.signature_ty(self.db))) | ||
254 | } else if let Some(active_parameter) = &self.active_parameter { | ||
255 | mark::hit!(active_param_type_match); | ||
256 | Some((active_parameter.name.clone(), active_parameter.ty.clone())) | ||
257 | } else { | ||
258 | None | ||
259 | } | ||
260 | } | ||
261 | |||
249 | fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { | 262 | fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { |
250 | let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); | 263 | let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); |
251 | let syntax_element = NodeOrToken::Token(fake_ident_token); | 264 | let syntax_element = NodeOrToken::Token(fake_ident_token); |
diff --git a/crates/completion/src/completion_item.rs b/crates/completion/src/completion_item.rs index f8be0ad2b..2e1ca0e59 100644 --- a/crates/completion/src/completion_item.rs +++ b/crates/completion/src/completion_item.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use std::fmt; | 3 | use std::fmt; |
4 | 4 | ||
5 | use hir::Documentation; | 5 | use hir::{Documentation, Mutability}; |
6 | use syntax::TextRange; | 6 | use syntax::TextRange; |
7 | use text_edit::TextEdit; | 7 | use text_edit::TextEdit; |
8 | 8 | ||
@@ -56,6 +56,10 @@ pub struct CompletionItem { | |||
56 | 56 | ||
57 | /// Score is useful to pre select or display in better order completion items | 57 | /// Score is useful to pre select or display in better order completion items |
58 | score: Option<CompletionScore>, | 58 | score: Option<CompletionScore>, |
59 | |||
60 | /// Indicates that a reference or mutable reference to this variable is a | ||
61 | /// possible match. | ||
62 | ref_match: Option<(Mutability, CompletionScore)>, | ||
59 | } | 63 | } |
60 | 64 | ||
61 | // We use custom debug for CompletionItem to make snapshot tests more readable. | 65 | // We use custom debug for CompletionItem to make snapshot tests more readable. |
@@ -194,6 +198,7 @@ impl CompletionItem { | |||
194 | deprecated: None, | 198 | deprecated: None, |
195 | trigger_call_info: None, | 199 | trigger_call_info: None, |
196 | score: None, | 200 | score: None, |
201 | ref_match: None, | ||
197 | } | 202 | } |
198 | } | 203 | } |
199 | /// What user sees in pop-up in the UI. | 204 | /// What user sees in pop-up in the UI. |
@@ -240,10 +245,15 @@ impl CompletionItem { | |||
240 | pub fn trigger_call_info(&self) -> bool { | 245 | pub fn trigger_call_info(&self) -> bool { |
241 | self.trigger_call_info | 246 | self.trigger_call_info |
242 | } | 247 | } |
248 | |||
249 | pub fn ref_match(&self) -> Option<(Mutability, CompletionScore)> { | ||
250 | self.ref_match | ||
251 | } | ||
243 | } | 252 | } |
244 | 253 | ||
245 | /// A helper to make `CompletionItem`s. | 254 | /// A helper to make `CompletionItem`s. |
246 | #[must_use] | 255 | #[must_use] |
256 | #[derive(Clone)] | ||
247 | pub(crate) struct Builder { | 257 | pub(crate) struct Builder { |
248 | source_range: TextRange, | 258 | source_range: TextRange, |
249 | completion_kind: CompletionKind, | 259 | completion_kind: CompletionKind, |
@@ -258,6 +268,7 @@ pub(crate) struct Builder { | |||
258 | deprecated: Option<bool>, | 268 | deprecated: Option<bool>, |
259 | trigger_call_info: Option<bool>, | 269 | trigger_call_info: Option<bool>, |
260 | score: Option<CompletionScore>, | 270 | score: Option<CompletionScore>, |
271 | ref_match: Option<(Mutability, CompletionScore)>, | ||
261 | } | 272 | } |
262 | 273 | ||
263 | impl Builder { | 274 | impl Builder { |
@@ -288,6 +299,7 @@ impl Builder { | |||
288 | deprecated: self.deprecated.unwrap_or(false), | 299 | deprecated: self.deprecated.unwrap_or(false), |
289 | trigger_call_info: self.trigger_call_info.unwrap_or(false), | 300 | trigger_call_info: self.trigger_call_info.unwrap_or(false), |
290 | score: self.score, | 301 | score: self.score, |
302 | ref_match: self.ref_match, | ||
291 | } | 303 | } |
292 | } | 304 | } |
293 | pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { | 305 | pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { |
@@ -350,6 +362,13 @@ impl Builder { | |||
350 | self.trigger_call_info = Some(true); | 362 | self.trigger_call_info = Some(true); |
351 | self | 363 | self |
352 | } | 364 | } |
365 | pub(crate) fn set_ref_match( | ||
366 | mut self, | ||
367 | ref_match: Option<(Mutability, CompletionScore)>, | ||
368 | ) -> Builder { | ||
369 | self.ref_match = ref_match; | ||
370 | self | ||
371 | } | ||
353 | } | 372 | } |
354 | 373 | ||
355 | impl<'a> Into<CompletionItem> for Builder { | 374 | impl<'a> Into<CompletionItem> for Builder { |
diff --git a/crates/completion/src/presentation.rs b/crates/completion/src/presentation.rs index 0a0dc1ce5..2a19281cf 100644 --- a/crates/completion/src/presentation.rs +++ b/crates/completion/src/presentation.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! This modules takes care of rendering various definitions as completion items. | 1 | //! This modules takes care of rendering various definitions as completion items. |
2 | //! It also handles scoring (sorting) completions. | 2 | //! It also handles scoring (sorting) completions. |
3 | 3 | ||
4 | use hir::{HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type}; | 4 | use hir::{HasAttrs, HasSource, HirDisplay, ModPath, Mutability, ScopeDef, StructKind, Type}; |
5 | use itertools::Itertools; | 5 | use itertools::Itertools; |
6 | use syntax::{ast::NameOwner, display::*}; | 6 | use syntax::{ast::NameOwner, display::*}; |
7 | use test_utils::mark; | 7 | use test_utils::mark; |
@@ -107,9 +107,16 @@ impl Completions { | |||
107 | } | 107 | } |
108 | }; | 108 | }; |
109 | 109 | ||
110 | let mut ref_match = None; | ||
110 | if let ScopeDef::Local(local) = resolution { | 111 | if let ScopeDef::Local(local) = resolution { |
111 | if let Some(score) = compute_score(ctx, &local.ty(ctx.db), &local_name) { | 112 | if let Some((active_name, active_type)) = ctx.active_name_and_type() { |
112 | completion_item = completion_item.set_score(score); | 113 | let ty = local.ty(ctx.db); |
114 | if let Some(score) = | ||
115 | compute_score_from_active(&active_type, &active_name, &ty, &local_name) | ||
116 | { | ||
117 | completion_item = completion_item.set_score(score); | ||
118 | } | ||
119 | ref_match = refed_type_matches(&active_type, &active_name, &ty, &local_name); | ||
113 | } | 120 | } |
114 | } | 121 | } |
115 | 122 | ||
@@ -131,7 +138,7 @@ impl Completions { | |||
131 | } | 138 | } |
132 | } | 139 | } |
133 | 140 | ||
134 | completion_item.kind(kind).set_documentation(docs).add_to(self) | 141 | completion_item.kind(kind).set_documentation(docs).set_ref_match(ref_match).add_to(self) |
135 | } | 142 | } |
136 | 143 | ||
137 | pub(crate) fn add_macro( | 144 | pub(crate) fn add_macro( |
@@ -342,25 +349,15 @@ impl Completions { | |||
342 | } | 349 | } |
343 | } | 350 | } |
344 | 351 | ||
345 | pub(crate) fn compute_score( | 352 | fn compute_score_from_active( |
346 | ctx: &CompletionContext, | 353 | active_type: &Type, |
354 | active_name: &str, | ||
347 | ty: &Type, | 355 | ty: &Type, |
348 | name: &str, | 356 | name: &str, |
349 | ) -> Option<CompletionScore> { | 357 | ) -> Option<CompletionScore> { |
350 | let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax { | ||
351 | mark::hit!(record_field_type_match); | ||
352 | let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?; | ||
353 | (struct_field.name(ctx.db).to_string(), struct_field.signature_ty(ctx.db)) | ||
354 | } else if let Some(active_parameter) = &ctx.active_parameter { | ||
355 | mark::hit!(active_param_type_match); | ||
356 | (active_parameter.name.clone(), active_parameter.ty.clone()) | ||
357 | } else { | ||
358 | return None; | ||
359 | }; | ||
360 | |||
361 | // Compute score | 358 | // Compute score |
362 | // For the same type | 359 | // For the same type |
363 | if &active_type != ty { | 360 | if active_type != ty { |
364 | return None; | 361 | return None; |
365 | } | 362 | } |
366 | 363 | ||
@@ -373,6 +370,24 @@ pub(crate) fn compute_score( | |||
373 | 370 | ||
374 | Some(res) | 371 | Some(res) |
375 | } | 372 | } |
373 | fn refed_type_matches( | ||
374 | active_type: &Type, | ||
375 | active_name: &str, | ||
376 | ty: &Type, | ||
377 | name: &str, | ||
378 | ) -> Option<(Mutability, CompletionScore)> { | ||
379 | let derefed_active = active_type.remove_ref()?; | ||
380 | let score = compute_score_from_active(&derefed_active, &active_name, &ty, &name)?; | ||
381 | Some(( | ||
382 | if active_type.is_mutable_reference() { Mutability::Mut } else { Mutability::Shared }, | ||
383 | score, | ||
384 | )) | ||
385 | } | ||
386 | |||
387 | fn compute_score(ctx: &CompletionContext, ty: &Type, name: &str) -> Option<CompletionScore> { | ||
388 | let (active_name, active_type) = ctx.active_name_and_type()?; | ||
389 | compute_score_from_active(&active_type, &active_name, ty, name) | ||
390 | } | ||
376 | 391 | ||
377 | enum Params { | 392 | enum Params { |
378 | Named(Vec<String>), | 393 | Named(Vec<String>), |