[java-completion] IDEA-283592 Method completion for toString() automatically invokes the toString generator - that should be optional

GitOrigin-RevId: 711dc0ded53119e338f844dcbcf6e78361e120fa
This commit is contained in:
Tagir Valeev
2021-12-02 13:31:44 +07:00
committed by intellij-monorepo-bot
parent ba50f67d55
commit dc3f7a9258
9 changed files with 124 additions and 41 deletions

View File

@@ -34,6 +34,7 @@ import org.jetbrains.java.generate.exception.GenerateCodeException;
import javax.swing.*;
import java.util.*;
import java.util.function.Consumer;
import static com.intellij.patterns.PlatformPatterns.psiElement;
@@ -153,7 +154,7 @@ public final class JavaGenerateMemberCompletionContributor {
insertGenerationInfos(context, Collections.singletonList(new PsiGenerationInfo<>(prototype)));
}
}, false, parent));
}, false, false, parent));
if (count++ > 100) return;
}
@@ -176,52 +177,68 @@ public final class JavaGenerateMemberCompletionContributor {
PsiClass baseClass = baseMethod.getContainingClass();
PsiSubstitutor substitutor = candidate.getSubstitutor();
if (!baseMethod.isConstructor() && baseClass != null && addedSignatures.add(baseMethod.getSignature(substitutor))) {
result.addElement(createOverridingLookupElement(implemented, baseMethod, baseClass, substitutor, generateDefaultMethods, parent));
result.addElement(
createOverridingLookupElement(implemented, baseMethod, baseClass, substitutor, generateDefaultMethods, parent, null));
if (GenerateEqualsHandler.hasNonStaticFields(parent)) {
if (MethodUtils.isEquals(baseMethod) || MethodUtils.isHashCode(baseMethod)) {
result.addElement(
createOverridingLookupElement(implemented, baseMethod, baseClass, substitutor, generateDefaultMethods, parent, context -> {
new GenerateEqualsHandler().invoke(context.getProject(), context.getEditor(), context.getFile());
}));
}
else if (MethodUtils.isToString(baseMethod)) {
result.addElement(
createOverridingLookupElement(implemented, baseMethod, baseClass, substitutor, generateDefaultMethods, parent, context -> {
new GenerateToStringActionHandlerImpl().invoke(context.getProject(), context.getEditor(), context.getFile());
}));
}
}
}
}
}
private static LookupElement createOverridingLookupElement(boolean implemented,
PsiMethod baseMethod,
PsiClass baseClass, PsiSubstitutor substitutor, boolean generateDefaultMethods, PsiClass targetClass) {
PsiClass baseClass,
PsiSubstitutor substitutor,
boolean generateDefaultMethods,
PsiClass targetClass,
@Nullable Consumer<InsertionContext> wizardRunner) {
RowIcon icon = IconManager
.getInstance().createRowIcon(baseMethod.getIcon(0), implemented ? AllIcons.Gutter.ImplementingMethod : AllIcons.Gutter.OverridingMethod);
return createGenerateMethodElement(baseMethod, substitutor, icon, baseClass.getName(), new InsertHandler<>() {
@Override
public void handleInsert(@NotNull InsertionContext context, @NotNull LookupElement item) {
removeLookupString(context);
.getInstance()
.createRowIcon(baseMethod.getIcon(0), implemented ? AllIcons.Gutter.ImplementingMethod : AllIcons.Gutter.OverridingMethod);
InsertHandler<LookupElement> handler;
if (wizardRunner != null) {
handler = new InsertHandler<>() {
@Override
public void handleInsert(@NotNull InsertionContext context, @NotNull LookupElement item) {
removeLookupString(context);
final PsiClass parent = PsiTreeUtil.findElementOfClassAtOffset(context.getFile(), context.getStartOffset(), PsiClass.class, false);
if (parent == null) return;
if (GenerateEqualsHandler.hasNonStaticFields(parent) && generateByWizards(context)) {
return;
}
try (AccessToken ignored = generateDefaultMethods ? forceDefaultMethodsInside() : AccessToken.EMPTY_ACCESS_TOKEN) {
List<PsiMethod> prototypes = OverrideImplementUtil.overrideOrImplementMethod(parent, baseMethod, false);
insertGenerationInfos(context, OverrideImplementUtil.convert2GenerationInfos(prototypes));
}
}
private boolean generateByWizards(@NotNull InsertionContext context) {
PsiFile file = context.getFile();
if (MethodUtils.isEquals(baseMethod) || MethodUtils.isHashCode(baseMethod)) {
context.setAddCompletionChar(false);
context.setLaterRunnable(() -> new GenerateEqualsHandler().invoke(context.getProject(), context.getEditor(), file));
return true;
context.setLaterRunnable(() -> wizardRunner.accept(context));
}
};
}
else {
handler = new InsertHandler<>() {
@Override
public void handleInsert(@NotNull InsertionContext context, @NotNull LookupElement item) {
removeLookupString(context);
if (MethodUtils.isToString(baseMethod)) {
context.setAddCompletionChar(false);
context.setLaterRunnable(() -> new GenerateToStringActionHandlerImpl().invoke(context.getProject(), context.getEditor(), file));
return true;
final PsiClass parent =
PsiTreeUtil.findElementOfClassAtOffset(context.getFile(), context.getStartOffset(), PsiClass.class, false);
if (parent == null) return;
try (AccessToken ignored = generateDefaultMethods ? forceDefaultMethodsInside() : AccessToken.EMPTY_ACCESS_TOKEN) {
List<PsiMethod> prototypes = OverrideImplementUtil.overrideOrImplementMethod(parent, baseMethod, false);
insertGenerationInfos(context, OverrideImplementUtil.convert2GenerationInfos(prototypes));
}
}
return false;
}
}, generateDefaultMethods, targetClass);
};
}
return createGenerateMethodElement(baseMethod, substitutor, icon, baseClass.getName(), handler, wizardRunner != null,
generateDefaultMethods, targetClass);
}
private static AccessToken forceDefaultMethodsInside() {
@@ -255,7 +272,9 @@ public final class JavaGenerateMemberCompletionContributor {
private static LookupElement createGenerateMethodElement(PsiMethod prototype,
PsiSubstitutor substitutor,
Icon icon,
String typeText, InsertHandler<LookupElement> insertHandler,
String typeText,
InsertHandler<LookupElement> insertHandler,
boolean generateByWizard,
boolean generateDefaultMethod,
PsiClass targetClass) {
String methodName = prototype.getName();
@@ -274,9 +293,13 @@ public final class JavaGenerateMemberCompletionContributor {
", ") + ")";
String overrideSignature = " @Override " + signature; // leading space to make it a middle match, under all annotation suggestions
String tailText = " " + (generateByWizard ? JavaBundle.message("completion.generate.via.wizard") : "{...}");
LookupElementBuilder element = LookupElementBuilder.create(prototype, signature).withLookupString(methodName).
withLookupString(signature).withLookupString(overrideSignature).withInsertHandler(insertHandler).
appendTailText(parameters, false).appendTailText(" {...}", true).withTypeText(typeText).withIcon(icon);
appendTailText(parameters, false).appendTailText(tailText, true).withTypeText(typeText).withIcon(icon);
if (generateByWizard) {
JavaMethodMergingContributor.disallowMerge(element);
}
if (prototype.isDeprecated()) {
element = element.withStrikeoutness(true);
}

View File

@@ -93,4 +93,13 @@ public class JavaMethodMergingContributor extends CompletionContributor implemen
Object o = item.getPsiElement();
return o instanceof PsiMethod ? (PsiMethod)o : null;
}
/**
* Mark item to forcefully disallow merge with another item that refers to the same PsiMethod.
*
* @param item to mark
*/
public static void disallowMerge(LookupElement item) {
item.putUserData(JavaCompletionUtil.FORCE_SHOW_SIGNATURE_ATTR, true);
}
}

View File

@@ -282,8 +282,7 @@ final class StreamConversion {
StreamMethodInvocation(LookupElement e, Consumer<? super InsertionContext> beforeInsertion) {
super(e);
myBeforeInsertion = beforeInsertion;
// Prevent from merging with non-stream method in JavaMethodMergingContributor
putUserData(JavaCompletionUtil.FORCE_SHOW_SIGNATURE_ATTR, true);
JavaMethodMergingContributor.disallowMerge(this);
}
@Override

View File

@@ -0,0 +1,4 @@
class A {
int a;
equal<caret>
}

View File

@@ -0,0 +1,8 @@
class A {
int a;
@Override
public boolean equals(Object obj) {
<selection><caret>return super.equals(obj);</selection>
}
}

View File

@@ -0,0 +1,4 @@
class A {
int a;
toStri<caret>
}

View File

@@ -0,0 +1,8 @@
class A {
int a;
@Override
public String toString() {
<selection><caret>return super.toString();</selection>
}
}

View File

@@ -1661,9 +1661,36 @@ class XInternalError {}
}
@NeedsIndex.SmartMode(reason = "JavaGenerateMemberCompletionContributor.fillCompletionVariants works in smart mode only (for equals() and hashCode())")
void testInvokeGenerateEqualsHashCodeOnOverrideCompletion() { doTest() }
void testInvokeGenerateEqualsHashCodeOnOverrideCompletion() {
configure()
assert myFixture.lookupElementStrings.size() == 2
lookup.setSelectedIndex(1)
type('\n')
checkResult()
}
@NeedsIndex.SmartMode(reason = "JavaGenerateMemberCompletionContributor.fillCompletionVariants works in smart mode only (for 'toString()')")
void testInvokeGenerateToStringOnOverrideCompletion() { doTest() }
void testInvokeGenerateToStringOnOverrideCompletion() {
configure()
assert myFixture.lookupElementStrings.size() == 2
lookup.setSelectedIndex(1)
type('\n')
checkResult()
}
@NeedsIndex.SmartMode(reason = "JavaGenerateMemberCompletionContributor.fillCompletionVariants works in smart mode only (for equals() and hashCode())")
void testDontGenerateEqualsHashCodeOnOverrideCompletion() {
configure()
type('\n')
checkResult()
}
@NeedsIndex.SmartMode(reason = "JavaGenerateMemberCompletionContributor.fillCompletionVariants works in smart mode only (for 'toString()')")
void testDontGenerateToStringOnOverrideCompletion() {
configure()
type('\n')
checkResult()
}
@NeedsIndex.SmartMode(reason = "JavaGenerateMemberCompletionContributor.fillCompletionVariants works in smart mode only (for getters and setters)")
void testAccessorViaCompletion() {

View File

@@ -1697,4 +1697,5 @@ megabytes.unit=megabytes
java.platform.module.system.name=Java Platform Module System
dialog.title.move.directory=Move Directory
progress.title.checking.if.class.exists=Check target class ''{0}'' exists
quickfix.find.cause.description=Attempts to highlight code elements that resulted in this warning and explain how exactly they contribute.
quickfix.find.cause.description=Attempts to highlight code elements that resulted in this warning and explain how exactly they contribute.
completion.generate.via.wizard=(generate via wizard)