IDEA-389301 [java-completion] JavaMethodHandleCompletionContributor -> JavaMethodHandleCompletionItemProvider

(part of) Convert reflection completion to ModCommand API

GitOrigin-RevId: 682a50d03da61d88cb6c7ace97aaf3c060bdbced
This commit is contained in:
Tagir Valeev
2026-05-15 19:01:02 +00:00
committed by intellij-monorepo-bot
parent bde5b6732e
commit e8fc23793b
3 changed files with 74 additions and 51 deletions
@@ -1144,8 +1144,8 @@
<multiHostInjector implementation="com.intellij.psi.impl.source.tree.injected.JavadocInjector"/>
<changeSignatureDetector language="JAVA" implementationClass="com.intellij.refactoring.changeSignature.JavaChangeSignatureDetector"/>
<lookup.charFilter implementation="com.intellij.codeInsight.completion.JavaCharFilter" id="java"/>
<completion.contributor language="JAVA" id="javaMethodHandle" order="last, before javaLegacy"
implementationClass="com.intellij.psi.impl.source.resolve.reference.impl.JavaMethodHandleCompletionContributor"/>
<modcompletion.completionItemProvider language="JAVA" id="javaMethodHandle" order="last"
implementationClass="com.intellij.psi.impl.source.resolve.reference.impl.JavaMethodHandleCompletionItemProvider"/>
<modcompletion.completionItemProvider language="JAVA" id="javaReflection"
implementationClass="com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionCompletionItemProvider"/>
<completion.contributor language="JAVA" id="javadoc" order="last, before javaLegacy, before wordCompletion"
@@ -6,13 +6,16 @@ import com.intellij.psi.PsiType;
import org.jetbrains.annotations.NotNullByDefault;
import org.jetbrains.annotations.Nullable;
/**
* Utilities to support mod command completion in Java
*/
@NotNullByDefault
final class JavaModCompletionUtils {
public final class JavaModCompletionUtils {
/**
* @param type type to represent as {@link MarkupText}
* @return markup text that represents the type
*/
static MarkupText typeMarkup(@Nullable PsiType type) {
public static MarkupText typeMarkup(@Nullable PsiType type) {
return type == null ? MarkupText.empty() :
MarkupText.plainText(type.getPresentableText());
}
@@ -1,18 +1,21 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.psi.impl.source.resolve.reference.impl;
import com.intellij.codeInsight.completion.CompletionContributor;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.completion.PrioritizedLookupElement;
import com.intellij.codeInsight.lookup.ExpressionLookupItem;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.java.completion.modcommand.JavaModCompletionUtils;
import com.intellij.modcompletion.CommonCompletionItem;
import com.intellij.modcompletion.ModCompletionItem;
import com.intellij.modcompletion.ModCompletionItemPresentation;
import com.intellij.modcompletion.ModCompletionItemProvider;
import com.intellij.modcompletion.ModCompletionResult;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.util.text.MarkupText;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.patterns.PsiJavaElementPattern;
import com.intellij.patterns.PsiMethodPattern;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementFactory;
import com.intellij.psi.PsiExpression;
@@ -22,10 +25,11 @@ import com.intellij.psi.PsiMethodCallExpression;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.PsiNameHelper;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.ui.IconManager;
import com.intellij.ui.PlatformIcons;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.NotNullByDefault;
import org.jetbrains.annotations.Nullable;
import javax.swing.Icon;
@@ -58,9 +62,10 @@ import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflection
import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionReferenceUtil.getMethodTypeExpressionText;
import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionReferenceUtil.getReflectiveClass;
import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionReferenceUtil.getTypeText;
import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionReferenceUtil.replaceText;
import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionReferenceUtil.shortenArgumentsClassReferences;
public final class JavaMethodHandleCompletionContributor extends CompletionContributor implements DumbAware {
@NotNullByDefault
public final class JavaMethodHandleCompletionItemProvider implements DumbAware, ModCompletionItemProvider {
// MethodHandle for constructors and methods
private static final Set<String> METHOD_HANDLE_FACTORY_NAMES = Set.of(
@@ -81,12 +86,12 @@ public final class JavaMethodHandleCompletionContributor extends CompletionContr
psiExpression().methodCallParameter(2, methodPattern(ArrayUtilRt.toStringArray(FIELD_HANDLE_FACTORY_NAMES))));
private static @NotNull PsiMethodPattern methodPattern(String... methodNames) {
private static PsiMethodPattern methodPattern(String... methodNames) {
return psiMethod().withName(methodNames).definedInClass(JAVA_LANG_INVOKE_METHOD_HANDLES_LOOKUP);
}
@Override
public void fillCompletionVariants(@NotNull CompletionParameters parameters, @NotNull CompletionResultSet result) {
public void provideItems(CompletionContext parameters, ModCompletionResult result) {
final PsiElement position = parameters.getPosition();
if (!isInJavaContext(position)) {
return;
@@ -100,7 +105,7 @@ public final class JavaMethodHandleCompletionContributor extends CompletionContr
}
}
private static void addMethodHandleVariants(@NotNull PsiElement position, @NotNull Consumer<? super LookupElement> result) {
private static void addMethodHandleVariants(PsiElement position, ModCompletionResult result) {
final PsiMethodCallExpression methodCall = PsiTreeUtil.getParentOfType(position, PsiMethodCallExpression.class);
if (methodCall != null) {
final String methodName = methodCall.getMethodExpression().getReferenceName();
@@ -123,23 +128,23 @@ public final class JavaMethodHandleCompletionContributor extends CompletionContr
}
}
private static void addConstructorSignatures(@NotNull ReflectiveClass ownerClass,
@NotNull PsiElement context,
@NotNull Consumer<? super LookupElement> result) {
private static void addConstructorSignatures(ReflectiveClass ownerClass,
PsiElement context,
ModCompletionResult result) {
final PsiMethod[] constructors = ownerClass.getPsiClass().getConstructors();
if (constructors.length != 0) {
lookupMethodTypes(Arrays.stream(constructors), context, result);
}
else {
result.consume(lookupSignature(ReflectiveSignature.NO_ARGUMENT_CONSTRUCTOR_SIGNATURE, context));
result.accept(lookupSignature(ReflectiveSignature.NO_ARGUMENT_CONSTRUCTOR_SIGNATURE, context));
}
}
private static void addMethodSignatures(@NotNull ReflectiveClass psiClass,
@NotNull String methodName,
private static void addMethodSignatures(ReflectiveClass psiClass,
String methodName,
boolean isStaticExpected,
@NotNull PsiElement context,
@NotNull Consumer<? super LookupElement> result) {
PsiElement context,
ModCompletionResult result) {
final PsiMethod[] methods = psiClass.getPsiClass().findMethodsByName(methodName, false);
if (methods.length != 0) {
final Stream<PsiMethod> methodStream = Arrays.stream(methods)
@@ -148,18 +153,18 @@ public final class JavaMethodHandleCompletionContributor extends CompletionContr
}
}
private static void lookupMethodTypes(@NotNull Stream<? extends PsiMethod> methods,
@NotNull PsiElement context,
@NotNull Consumer<? super LookupElement> result) {
private static void lookupMethodTypes(Stream<? extends PsiMethod> methods,
PsiElement context,
ModCompletionResult result) {
methods
.map(JavaReflectionReferenceUtil::getMethodSignature)
.filter(Objects::nonNull)
.sorted(ReflectiveSignature::compareTo)
.map(signature -> lookupSignature(signature, context))
.forEach(result::consume);
.forEach(result);
}
private static @NotNull LookupElement lookupSignature(@NotNull ReflectiveSignature signature, @NotNull PsiElement context) {
private static ModCompletionItem lookupSignature(ReflectiveSignature signature, PsiElement context) {
final String expressionText = getMethodTypeExpressionText(signature);
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(context.getProject());
final PsiExpression expression = factory.createExpressionFromText(expressionText, context);
@@ -168,10 +173,10 @@ public final class JavaMethodHandleCompletionContributor extends CompletionContr
final String presentableText = PsiNameHelper.getShortClassName(JAVA_LANG_INVOKE_METHOD_TYPE) + "." + METHOD_TYPE + shortTypes;
final String lookupText = METHOD_TYPE + signature.getText(true, PsiNameHelper::getShortClassName);
return lookupExpression(expression, IconManager.getInstance().getPlatformIcon(com.intellij.ui.PlatformIcons.Method), presentableText, lookupText);
return lookupExpression(expression, IconManager.getInstance().getPlatformIcon(PlatformIcons.Method), presentableText, lookupText);
}
private static void addFieldHandleVariants(@NotNull PsiElement position, @NotNull Consumer<? super LookupElement> result) {
private static void addFieldHandleVariants(PsiElement position, ModCompletionResult result) {
final PsiMethodCallExpression methodCall = PsiTreeUtil.getParentOfType(position, PsiMethodCallExpression.class);
if (methodCall != null) {
final String methodName = methodCall.getMethodExpression().getReferenceName();
@@ -190,10 +195,10 @@ public final class JavaMethodHandleCompletionContributor extends CompletionContr
}
}
private static void addFieldType(@NotNull ReflectiveClass ownerClass,
@NotNull String fieldName,
@NotNull PsiElement context,
@NotNull Consumer<? super LookupElement> result) {
private static void addFieldType(ReflectiveClass ownerClass,
String fieldName,
PsiElement context,
ModCompletionResult result) {
final PsiField field = ownerClass.getPsiClass().findFieldByName(fieldName, false);
if (field != null) {
final String typeText = getTypeText(field.getType());
@@ -201,22 +206,37 @@ public final class JavaMethodHandleCompletionContributor extends CompletionContr
final PsiExpression expression = factory.createExpressionFromText(typeText + ".class", context);
final String shortType = PsiNameHelper.getShortClassName(typeText);
result.consume(lookupExpression(expression, IconManager.getInstance().getPlatformIcon(com.intellij.ui.PlatformIcons.Class), shortType + ".class", shortType));
result.accept(lookupExpression(expression, IconManager.getInstance().getPlatformIcon(PlatformIcons.Class), shortType + ".class", shortType));
}
}
private static @NotNull LookupElement lookupExpression(@NotNull PsiExpression expression,
@Nullable Icon icon,
@NotNull String presentableText,
@NotNull String lookupText) {
final LookupElement element = new ExpressionLookupItem(expression, icon, presentableText, lookupText) {
@Override
public void handleInsert(@NotNull InsertionContext context) {
context.getDocument().deleteString(context.getStartOffset(), context.getTailOffset());
context.commitDocument();
replaceText(context, getObject().getText());
}
};
return PrioritizedLookupElement.withPriority(element, 1);
private static ModCompletionItem lookupExpression(PsiExpression expression,
@Nullable Icon icon,
@NlsSafe String presentableText,
String lookupText) {
String text = expression.getText();
return new CommonCompletionItem(lookupText)
.withPresentation(new ModCompletionItemPresentation(MarkupText.plainText(presentableText))
.withMainIcon(() -> icon)
.withDetailText(JavaModCompletionUtils.typeMarkup(expression.getType())))
.withPriority(1)
.withAdditionalUpdater(((completionStart, updater) -> {
Document document = updater.getDocument();
document.deleteString(completionStart, updater.getCaretOffset());
PsiDocumentManager.getInstance(updater.getProject()).commitDocument(document);
PsiElement newElement = PsiUtilCore.getElementAtOffset(updater.getPsiFile(), completionStart);
PsiElement params = newElement.getParent().getParent();
int end = params.getTextRange().getEndOffset() - 1;
int start = Math.min(newElement.getTextRange().getEndOffset(), end);
document.replaceString(start, end, text);
PsiDocumentManager.getInstance(updater.getProject()).commitDocument(document);
shortenArgumentsClassReferences(updater);
}));
}
@Override
public boolean isEnabled() {
return true;
}
}