Unnamed repository; edit this file 'description' to name the repository.
add different completion for fn fields
| -rw-r--r-- | crates/ide-completion/src/completions/dot.rs | 34 | ||||
| -rw-r--r-- | crates/ide-completion/src/render.rs | 19 |
2 files changed, 52 insertions, 1 deletions
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index 5bcc867fe1..12591449cd 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -28,6 +28,13 @@ pub(crate) fn complete_dot( if let DotAccessKind::Method { .. } = dot_access.kind { cov_mark::hit!(test_no_struct_field_completion_for_method_call); + complete_fn_fields( + acc, + ctx, + receiver_ty, + |acc, field, ty| acc.add_field(ctx, dot_access, None, field, &ty), + |acc, field, ty| acc.add_tuple_field(ctx, None, field, &ty), + ); } else { complete_fields( acc, @@ -144,6 +151,33 @@ fn complete_methods( ); } +fn complete_fn_fields( + acc: &mut Completions, + ctx: &CompletionContext<'_>, + receiver: &hir::Type, + mut named_field: impl FnMut(&mut Completions, hir::Field, hir::Type), + mut tuple_index: impl FnMut(&mut Completions, usize, hir::Type), +) { + let mut seen_names = FxHashSet::default(); + for receiver in receiver.autoderef(ctx.db) { + for (field, ty) in receiver.fields(ctx.db) { + if seen_names.insert(field.name(ctx.db)) && (ty.is_fn() || ty.is_closure()) { + named_field(acc, field, ty); + } + } + for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() { + // Tuples are always the last type in a deref chain, so just check if the name is + // already seen without inserting into the hashset. + if !seen_names.contains(&hir::Name::new_tuple_field(i)) + && (ty.is_fn() || ty.is_closure()) + { + // Tuple fields are always public (tuple struct fields are handled above). + tuple_index(acc, i, ty); + } + } + } +} + #[cfg(test)] mod tests { use expect_test::{expect, Expect}; diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 00a9081985..f05cc78ac6 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -18,6 +18,7 @@ use ide_db::{ RootDatabase, SnippetCap, SymbolKind, }; use syntax::{AstNode, SmolStr, SyntaxKind, TextRange}; +use text_edit::TextEdit; use crate::{ context::{DotAccess, PathCompletionCtx, PathKind, PatternContext}, @@ -147,7 +148,23 @@ pub(crate) fn render_field( .set_documentation(field.docs(db)) .set_deprecated(is_deprecated) .lookup_by(name); - item.insert_text(field_with_receiver(db, receiver.as_ref(), &escaped_name)); + if ty.is_fn() || ty.is_closure() { + let mut builder = TextEdit::builder(); + // Use TextEdit to insert / replace the ranges: + // 1. Insert one character ('(') before start of struct name + // 2. Insert one character (')') before call parens + // 3. Variable character of the actual field name + // 4. Optionally, two character ('()') for fn call + // + // TODO: Find a way to get the above ranges, especially the first two + builder.replace( + ctx.source_range(), + field_with_receiver(db, receiver.as_ref(), &escaped_name).into(), + ); + item.text_edit(builder.finish()); + } else { + item.insert_text(field_with_receiver(db, receiver.as_ref(), &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) { |