diff options
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 | 121 |
3 files changed, 128 insertions, 27 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..0a6f5a1ea 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( |
@@ -297,9 +304,14 @@ impl Completions { | |||
297 | ) { | 304 | ) { |
298 | let is_deprecated = is_deprecated(variant, ctx.db); | 305 | let is_deprecated = is_deprecated(variant, ctx.db); |
299 | let name = local_name.unwrap_or_else(|| variant.name(ctx.db).to_string()); | 306 | let name = local_name.unwrap_or_else(|| variant.name(ctx.db).to_string()); |
300 | let qualified_name = match &path { | 307 | let (qualified_name, short_qualified_name) = match &path { |
301 | Some(it) => it.to_string(), | 308 | Some(path) => { |
302 | None => name.to_string(), | 309 | let full = path.to_string(); |
310 | let short = | ||
311 | path.segments[path.segments.len().saturating_sub(2)..].iter().join("::"); | ||
312 | (full, short) | ||
313 | } | ||
314 | None => (name.to_string(), name.to_string()), | ||
303 | }; | 315 | }; |
304 | let detail_types = variant | 316 | let detail_types = variant |
305 | .fields(ctx.db) | 317 | .fields(ctx.db) |
@@ -328,39 +340,27 @@ impl Completions { | |||
328 | .set_deprecated(is_deprecated) | 340 | .set_deprecated(is_deprecated) |
329 | .detail(detail); | 341 | .detail(detail); |
330 | 342 | ||
331 | if path.is_some() { | ||
332 | res = res.lookup_by(name); | ||
333 | } | ||
334 | |||
335 | if variant_kind == StructKind::Tuple { | 343 | if variant_kind == StructKind::Tuple { |
336 | mark::hit!(inserts_parens_for_tuple_enums); | 344 | mark::hit!(inserts_parens_for_tuple_enums); |
337 | let params = Params::Anonymous(variant.fields(ctx.db).len()); | 345 | let params = Params::Anonymous(variant.fields(ctx.db).len()); |
338 | res = res.add_call_parens(ctx, qualified_name, params) | 346 | res = res.add_call_parens(ctx, short_qualified_name, params) |
347 | } else if path.is_some() { | ||
348 | res = res.lookup_by(short_qualified_name); | ||
339 | } | 349 | } |
340 | 350 | ||
341 | res.add_to(self); | 351 | res.add_to(self); |
342 | } | 352 | } |
343 | } | 353 | } |
344 | 354 | ||
345 | pub(crate) fn compute_score( | 355 | fn compute_score_from_active( |
346 | ctx: &CompletionContext, | 356 | active_type: &Type, |
357 | active_name: &str, | ||
347 | ty: &Type, | 358 | ty: &Type, |
348 | name: &str, | 359 | name: &str, |
349 | ) -> Option<CompletionScore> { | 360 | ) -> 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 | 361 | // Compute score |
362 | // For the same type | 362 | // For the same type |
363 | if &active_type != ty { | 363 | if active_type != ty { |
364 | return None; | 364 | return None; |
365 | } | 365 | } |
366 | 366 | ||
@@ -373,6 +373,24 @@ pub(crate) fn compute_score( | |||
373 | 373 | ||
374 | Some(res) | 374 | Some(res) |
375 | } | 375 | } |
376 | fn refed_type_matches( | ||
377 | active_type: &Type, | ||
378 | active_name: &str, | ||
379 | ty: &Type, | ||
380 | name: &str, | ||
381 | ) -> Option<(Mutability, CompletionScore)> { | ||
382 | let derefed_active = active_type.remove_ref()?; | ||
383 | let score = compute_score_from_active(&derefed_active, &active_name, &ty, &name)?; | ||
384 | Some(( | ||
385 | if active_type.is_mutable_reference() { Mutability::Mut } else { Mutability::Shared }, | ||
386 | score, | ||
387 | )) | ||
388 | } | ||
389 | |||
390 | fn compute_score(ctx: &CompletionContext, ty: &Type, name: &str) -> Option<CompletionScore> { | ||
391 | let (active_name, active_type) = ctx.active_name_and_type()?; | ||
392 | compute_score_from_active(&active_type, &active_name, ty, name) | ||
393 | } | ||
376 | 394 | ||
377 | enum Params { | 395 | enum Params { |
378 | Named(Vec<String>), | 396 | Named(Vec<String>), |
@@ -592,6 +610,57 @@ fn main() { Foo::Fo<|> } | |||
592 | } | 610 | } |
593 | 611 | ||
594 | #[test] | 612 | #[test] |
613 | fn lookup_enums_by_two_qualifiers() { | ||
614 | check( | ||
615 | r#" | ||
616 | mod m { | ||
617 | pub enum Spam { Foo, Bar(i32) } | ||
618 | } | ||
619 | fn main() { let _: m::Spam = S<|> } | ||
620 | "#, | ||
621 | expect![[r#" | ||
622 | [ | ||
623 | CompletionItem { | ||
624 | label: "Spam::Bar(…)", | ||
625 | source_range: 75..76, | ||
626 | delete: 75..76, | ||
627 | insert: "Spam::Bar($0)", | ||
628 | kind: EnumVariant, | ||
629 | lookup: "Spam::Bar", | ||
630 | detail: "(i32)", | ||
631 | trigger_call_info: true, | ||
632 | }, | ||
633 | CompletionItem { | ||
634 | label: "m", | ||
635 | source_range: 75..76, | ||
636 | delete: 75..76, | ||
637 | insert: "m", | ||
638 | kind: Module, | ||
639 | }, | ||
640 | CompletionItem { | ||
641 | label: "m::Spam::Foo", | ||
642 | source_range: 75..76, | ||
643 | delete: 75..76, | ||
644 | insert: "m::Spam::Foo", | ||
645 | kind: EnumVariant, | ||
646 | lookup: "Spam::Foo", | ||
647 | detail: "()", | ||
648 | }, | ||
649 | CompletionItem { | ||
650 | label: "main()", | ||
651 | source_range: 75..76, | ||
652 | delete: 75..76, | ||
653 | insert: "main()$0", | ||
654 | kind: Function, | ||
655 | lookup: "main", | ||
656 | detail: "fn main()", | ||
657 | }, | ||
658 | ] | ||
659 | "#]], | ||
660 | ) | ||
661 | } | ||
662 | |||
663 | #[test] | ||
595 | fn sets_deprecated_flag_in_completion_items() { | 664 | fn sets_deprecated_flag_in_completion_items() { |
596 | check( | 665 | check( |
597 | r#" | 666 | r#" |