aboutsummaryrefslogtreecommitdiff
path: root/crates/completion
diff options
context:
space:
mode:
Diffstat (limited to 'crates/completion')
-rw-r--r--crates/completion/src/completion_context.rs13
-rw-r--r--crates/completion/src/completion_item.rs21
-rw-r--r--crates/completion/src/presentation.rs121
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
3use std::fmt; 3use std::fmt;
4 4
5use hir::Documentation; 5use hir::{Documentation, Mutability};
6use syntax::TextRange; 6use syntax::TextRange;
7use text_edit::TextEdit; 7use 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)]
247pub(crate) struct Builder { 257pub(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
263impl Builder { 274impl 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
355impl<'a> Into<CompletionItem> for Builder { 374impl<'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
4use hir::{HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type}; 4use hir::{HasAttrs, HasSource, HirDisplay, ModPath, Mutability, ScopeDef, StructKind, Type};
5use itertools::Itertools; 5use itertools::Itertools;
6use syntax::{ast::NameOwner, display::*}; 6use syntax::{ast::NameOwner, display::*};
7use test_utils::mark; 7use 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
345pub(crate) fn compute_score( 355fn 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}
376fn 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
390fn 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
377enum Params { 395enum 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#"
616mod m {
617 pub enum Spam { Foo, Bar(i32) }
618}
619fn 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#"