Unnamed repository; edit this file 'description' to name the repository.
add different completion for fn fields
dfireBird 2023-11-28
parent c7c582a · commit a0e690a
-rw-r--r--crates/ide-completion/src/completions/dot.rs34
-rw-r--r--crates/ide-completion/src/render.rs19
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) {