Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-completion/src/render.rs')
| -rw-r--r-- | crates/ide-completion/src/render.rs | 140 |
1 files changed, 92 insertions, 48 deletions
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index c239ca512d..61e8114d38 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -18,7 +18,7 @@ use ide_db::{ imports::import_assets::LocatedImport, RootDatabase, SnippetCap, SymbolKind, }; -use syntax::{ast, format_smolstr, AstNode, Edition, SmolStr, SyntaxKind, TextRange, ToSmolStr}; +use syntax::{ast, format_smolstr, AstNode, SmolStr, SyntaxKind, TextRange, ToSmolStr}; use crate::{ context::{DotAccess, DotAccessKind, PathCompletionCtx, PathKind, PatternContext}, @@ -28,7 +28,8 @@ use crate::{ literal::render_variant_lit, macro_::{render_macro, render_macro_pat}, }, - CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance, + CompletionContext, CompletionItem, CompletionItemKind, CompletionItemRefMode, + CompletionRelevance, }; /// Interface for data and methods required for items rendering. #[derive(Debug, Clone)] @@ -122,7 +123,7 @@ impl<'a> RenderContext<'a> { pub(crate) fn render_field( ctx: RenderContext<'_>, dot_access: &DotAccess, - receiver: Option<hir::Name>, + receiver: Option<SmolStr>, field: hir::Field, ty: &hir::Type, ) -> CompletionItem { @@ -136,7 +137,7 @@ pub(crate) fn render_field( let mut item = CompletionItem::new( SymbolKind::Field, ctx.source_range(), - field_with_receiver(db, receiver.as_ref(), &name, ctx.completion.edition), + field_with_receiver(receiver.as_deref(), &name), ctx.completion.edition, ); item.set_relevance(CompletionRelevance { @@ -158,8 +159,7 @@ pub(crate) fn render_field( builder.replace( ctx.source_range(), - field_with_receiver(db, receiver.as_ref(), &escaped_name, ctx.completion.edition) - .into(), + field_with_receiver(receiver.as_deref(), &escaped_name).into(), ); let expected_fn_type = @@ -183,17 +183,12 @@ pub(crate) fn render_field( item.text_edit(builder.finish()); } else { - item.insert_text(field_with_receiver( - db, - receiver.as_ref(), - &escaped_name, - ctx.completion.edition, - )); + item.insert_text(field_with_receiver(receiver.as_deref(), &escaped_name)); } if let Some(receiver) = &dot_access.receiver { if let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) { - if let Some(ref_match) = compute_ref_match(ctx.completion, ty) { - item.ref_match(ref_match, original.syntax().text_range().start()); + if let Some(ref_mode) = compute_ref_match(ctx.completion, ty) { + item.ref_match(ref_mode, original.syntax().text_range().start()); } } } @@ -201,33 +196,21 @@ pub(crate) fn render_field( item.build(db) } -fn field_with_receiver( - db: &RootDatabase, - receiver: Option<&hir::Name>, - field_name: &str, - edition: Edition, -) -> SmolStr { - receiver.map_or_else( - || field_name.into(), - |receiver| format_smolstr!("{}.{field_name}", receiver.display(db, edition)), - ) +fn field_with_receiver(receiver: Option<&str>, field_name: &str) -> SmolStr { + receiver + .map_or_else(|| field_name.into(), |receiver| format_smolstr!("{}.{field_name}", receiver)) } pub(crate) fn render_tuple_field( ctx: RenderContext<'_>, - receiver: Option<hir::Name>, + receiver: Option<SmolStr>, field: usize, ty: &hir::Type, ) -> CompletionItem { let mut item = CompletionItem::new( SymbolKind::Field, ctx.source_range(), - field_with_receiver( - ctx.db(), - receiver.as_ref(), - &field.to_string(), - ctx.completion.edition, - ), + field_with_receiver(receiver.as_deref(), &field.to_string()), ctx.completion.edition, ); item.detail(ty.display(ctx.db(), ctx.completion.edition).to_string()) @@ -440,7 +423,7 @@ fn render_resolution_path( let name = local_name.display_no_db(ctx.completion.edition).to_smolstr(); let mut item = render_resolution_simple_(ctx, &local_name, import_to_add, resolution); - if local_name.is_escaped(completion.edition) { + if local_name.needs_escape(completion.edition) { item.insert_text(local_name.display_no_db(completion.edition).to_smolstr()); } // Add `<>` for generic types @@ -638,20 +621,34 @@ fn compute_exact_name_match(ctx: &CompletionContext<'_>, completion_name: &str) fn compute_ref_match( ctx: &CompletionContext<'_>, completion_ty: &hir::Type, -) -> Option<hir::Mutability> { +) -> Option<CompletionItemRefMode> { let expected_type = ctx.expected_type.as_ref()?; - if completion_ty != expected_type { - let expected_type_without_ref = expected_type.remove_ref()?; - if completion_ty.autoderef(ctx.db).any(|deref_ty| deref_ty == expected_type_without_ref) { + let expected_without_ref = expected_type.remove_ref(); + let completion_without_ref = completion_ty.remove_ref(); + + if completion_ty == expected_type { + return None; + } + + if let Some(expected_without_ref) = &expected_without_ref { + if completion_ty.autoderef(ctx.db).any(|ty| ty == *expected_without_ref) { cov_mark::hit!(suggest_ref); let mutability = if expected_type.is_mutable_reference() { hir::Mutability::Mut } else { hir::Mutability::Shared }; - return Some(mutability); - }; + return Some(CompletionItemRefMode::Reference(mutability)); + } + } + + if let Some(completion_without_ref) = completion_without_ref { + if completion_without_ref == *expected_type && completion_without_ref.is_copy(ctx.db) { + cov_mark::hit!(suggest_deref); + return Some(CompletionItemRefMode::Dereference); + } } + None } @@ -664,16 +661,16 @@ fn path_ref_match( if let Some(original_path) = &path_ctx.original_path { // At least one char was typed by the user already, in that case look for the original path if let Some(original_path) = completion.sema.original_ast_node(original_path.clone()) { - if let Some(ref_match) = compute_ref_match(completion, ty) { - item.ref_match(ref_match, original_path.syntax().text_range().start()); + if let Some(ref_mode) = compute_ref_match(completion, ty) { + item.ref_match(ref_mode, original_path.syntax().text_range().start()); } } } else { // completion requested on an empty identifier, there is no path here yet. // FIXME: This might create inconsistent completions where we show a ref match in macro inputs // as long as nothing was typed yet - if let Some(ref_match) = compute_ref_match(completion, ty) { - item.ref_match(ref_match, completion.position.offset); + if let Some(ref_mode) = compute_ref_match(completion, ty) { + item.ref_match(ref_mode, completion.position.offset); } } } @@ -693,20 +690,28 @@ mod tests { }; #[track_caller] - fn check(ra_fixture: &str, kind: impl Into<CompletionItemKind>, expect: Expect) { + fn check( + #[rust_analyzer::rust_fixture] ra_fixture: &str, + kind: impl Into<CompletionItemKind>, + expect: Expect, + ) { let actual = do_completion(ra_fixture, kind.into()); expect.assert_debug_eq(&actual); } #[track_caller] - fn check_kinds(ra_fixture: &str, kinds: &[CompletionItemKind], expect: Expect) { + fn check_kinds( + #[rust_analyzer::rust_fixture] ra_fixture: &str, + kinds: &[CompletionItemKind], + expect: Expect, + ) { let actual: Vec<_> = kinds.iter().flat_map(|&kind| do_completion(ra_fixture, kind)).collect(); expect.assert_debug_eq(&actual); } #[track_caller] - fn check_function_relevance(ra_fixture: &str, expect: Expect) { + fn check_function_relevance(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { let actual: Vec<_> = do_completion(ra_fixture, CompletionItemKind::SymbolKind(SymbolKind::Method)) .into_iter() @@ -717,7 +722,11 @@ mod tests { } #[track_caller] - fn check_relevance_for_kinds(ra_fixture: &str, kinds: &[CompletionItemKind], expect: Expect) { + fn check_relevance_for_kinds( + #[rust_analyzer::rust_fixture] ra_fixture: &str, + kinds: &[CompletionItemKind], + expect: Expect, + ) { let mut actual = get_all_items(TEST_CONFIG, ra_fixture, None); actual.retain(|it| kinds.contains(&it.kind)); actual.sort_by_key(|it| cmp::Reverse(it.relevance.score())); @@ -725,7 +734,7 @@ mod tests { } #[track_caller] - fn check_relevance(ra_fixture: &str, expect: Expect) { + fn check_relevance(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { let mut actual = get_all_items(TEST_CONFIG, ra_fixture, None); actual.retain(|it| it.kind != CompletionItemKind::Snippet); actual.retain(|it| it.kind != CompletionItemKind::Keyword); @@ -2053,7 +2062,42 @@ fn main() { } #[test] - fn suggest_deref() { + fn suggest_deref_copy() { + cov_mark::check!(suggest_deref); + check_relevance( + r#" +//- minicore: copy +struct Foo; + +impl Copy for Foo {} +impl Clone for Foo { + fn clone(&self) -> Self { *self } +} + +fn bar(x: Foo) {} + +fn main() { + let foo = &Foo; + bar($0); +} +"#, + expect![[r#" + st Foo Foo [type] + st Foo Foo [type] + ex Foo [type] + lc foo &Foo [local] + lc *foo [type+local] + fn bar(…) fn(Foo) [] + fn main() fn() [] + md core [] + tt Clone [] + tt Copy [] + "#]], + ); + } + + #[test] + fn suggest_deref_trait() { check_relevance( r#" //- minicore: deref |