mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Java: "Generate overloaded constructor with default parameter values" for records (IDEA-360071)
GitOrigin-RevId: a20fd5478241de404df3243f1b8b9a7d1a73829e
This commit is contained in:
committed by
intellij-monorepo-bot
parent
95ed423db6
commit
dfae4a26c2
+59
-25
@@ -18,6 +18,7 @@ import com.intellij.pom.java.JavaFeature;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.refactoring.util.RefactoringUIUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.CommonJavaRefactoringUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
@@ -42,31 +43,52 @@ public final class DefineParamsDefaultValueAction extends PsiBasedModCommandActi
|
||||
@Override
|
||||
protected @Nullable Presentation getPresentation(@NotNull ActionContext context, @NotNull PsiElement element) {
|
||||
if (!JavaLanguage.INSTANCE.equals(element.getLanguage())) return null;
|
||||
final PsiElement parent = PsiTreeUtil.getParentOfType(element, PsiMethod.class, PsiCodeBlock.class);
|
||||
if (!(parent instanceof PsiMethod method)) return null;
|
||||
final PsiParameterList parameterList = method.getParameterList();
|
||||
if (parameterList.isEmpty()) return null;
|
||||
final PsiClass containingClass = method.getContainingClass();
|
||||
if (containingClass == null || (containingClass.isInterface() && !PsiUtil.isAvailable(JavaFeature.EXTENSION_METHODS, method))) {
|
||||
final PsiElement parent = PsiTreeUtil.getParentOfType(element, PsiMethod.class, PsiClass.class, PsiCodeBlock.class);
|
||||
String message;
|
||||
if (parent instanceof PsiMethod method) {
|
||||
final PsiParameterList parameterList = method.getParameterList();
|
||||
if (parameterList.isEmpty()) return null;
|
||||
final PsiClass containingClass = method.getContainingClass();
|
||||
if (containingClass == null || (containingClass.isInterface() && !PsiUtil.isAvailable(JavaFeature.EXTENSION_METHODS, method))) {
|
||||
return null;
|
||||
}
|
||||
if ((containingClass instanceof PsiImplicitClass || containingClass instanceof PsiAnonymousClass) && method.isConstructor()) {
|
||||
return null; // constructors can't be declared here, code is broken so don't suggest generating more broken code
|
||||
}
|
||||
if (containingClass.isAnnotationType()) {
|
||||
// Method with parameters in annotation is a compilation error; there's no sense to create overload
|
||||
return null;
|
||||
}
|
||||
message = QuickFixBundle.message("generate.overloaded.method.or.constructor.with.default.parameter.values",
|
||||
JavaElementKind.fromElement(method).lessDescriptive().object());
|
||||
}
|
||||
else if (parent instanceof PsiClass aClass && aClass.isRecord()) {
|
||||
PsiRecordHeader header = aClass.getRecordHeader();
|
||||
if (header == null || header.getTextOffset() + header.getTextLength() < element.getTextOffset()) return null;
|
||||
if (header.getRecordComponents().length == 0) return null;
|
||||
message = QuickFixBundle.message("generate.overloaded.method.or.constructor.with.default.parameter.values",
|
||||
JavaElementKind.CONSTRUCTOR.lessDescriptive().object());
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
if ((containingClass instanceof PsiImplicitClass || containingClass instanceof PsiAnonymousClass) && method.isConstructor()) {
|
||||
return null; // constructors can't be declared here, code is broken so don't suggest generating more broken code
|
||||
}
|
||||
if (containingClass.isAnnotationType()) {
|
||||
// Method with parameters in annotation is a compilation error; there's no sense to create overload
|
||||
return null;
|
||||
}
|
||||
return Presentation.of(QuickFixBundle.message("generate.overloaded.method.or.constructor.with.default.parameter.values",
|
||||
JavaElementKind.fromElement(method).lessDescriptive().object()))
|
||||
return Presentation.of(message)
|
||||
.withIcon(AllIcons.Actions.RefactoringBulb)
|
||||
.withPriority(PriorityAction.Priority.LOW);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull ModCommand perform(@NotNull ActionContext context, @NotNull PsiElement element) {
|
||||
PsiMethod method = PsiTreeUtil.getParentOfType(element, PsiMethod.class);
|
||||
assert method != null;
|
||||
PsiElement parent = PsiTreeUtil.getParentOfType(element, PsiMethod.class, PsiClass.class);
|
||||
PsiMethod method;
|
||||
if (parent instanceof PsiMethod) {
|
||||
method = (PsiMethod)parent;
|
||||
}
|
||||
else if (parent instanceof PsiClass aClass && aClass.isRecord()) {
|
||||
method = JavaPsiRecordUtil.findCanonicalConstructor(aClass);
|
||||
assert method != null;
|
||||
}
|
||||
else throw new AssertionError();
|
||||
PsiParameterList parameterList = method.getParameterList();
|
||||
PsiParameter[] parameters = parameterList.getParameters();
|
||||
if (parameters.length == 1) {
|
||||
@@ -74,23 +96,35 @@ public final class DefineParamsDefaultValueAction extends PsiBasedModCommandActi
|
||||
updater.getWritable(m).getParameterList().getParameters()));
|
||||
}
|
||||
List<ParameterClassMember> members = ContainerUtil.map(parameters, ParameterClassMember::new);
|
||||
PsiParameter selectedParam = PsiTreeUtil.getParentOfType(element, PsiParameter.class);
|
||||
int idx = selectedParam != null ? ArrayUtil.find(parameters, selectedParam) : -1;
|
||||
int idx = getSelectedIndex(element);
|
||||
List<ParameterClassMember> defaultSelection = idx >= 0 ? List.of(members.get(idx)) : members;
|
||||
return ModCommand.chooseMultipleMembers(
|
||||
QuickFixBundle.message("choose.default.value.parameters.popup.title"),
|
||||
members, defaultSelection,
|
||||
sel -> ModCommand.psiUpdate(context, updater -> {
|
||||
invoke(context.project(), updater.getWritable(element), updater,
|
||||
invoke(context.project(), updater.getWritable(method), updater,
|
||||
ContainerUtil.map2Array(sel, PsiParameter.EMPTY_ARRAY,
|
||||
s -> updater.getWritable(((ParameterClassMember)s).getParameter())));
|
||||
}));
|
||||
}
|
||||
|
||||
private static void invoke(@NotNull Project project, @NotNull PsiElement element,
|
||||
private static int getSelectedIndex(@NotNull PsiElement element) {
|
||||
PsiVariable selected = PsiTreeUtil.getParentOfType(element, PsiParameter.class, PsiRecordComponent.class);
|
||||
if (selected instanceof PsiParameter parameter) {
|
||||
PsiParameterList parameterList = (PsiParameterList)parameter.getParent();
|
||||
return parameterList.getParameterIndex(parameter);
|
||||
}
|
||||
else if (selected instanceof PsiRecordComponent recordComponent) {
|
||||
PsiRecordHeader recordHeader = (PsiRecordHeader)recordComponent.getParent();
|
||||
return ArrayUtil.find(recordHeader.getRecordComponents(), recordComponent);
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void invoke(@NotNull Project project, @NotNull PsiMethod method,
|
||||
@NotNull ModPsiUpdater updater, @NotNull PsiParameter @NotNull [] parameters) {
|
||||
final PsiMethod method = PsiTreeUtil.getNonStrictParentOfType(element, PsiMethod.class);
|
||||
assert method != null;
|
||||
PsiParameterList parameterList = method.getParameterList();
|
||||
if (parameters.length == 0) return;
|
||||
final PsiMethod methodPrototype = generateMethodPrototype(method, parameters);
|
||||
@@ -99,8 +133,8 @@ public final class DefineParamsDefaultValueAction extends PsiBasedModCommandActi
|
||||
for (PsiMethod existingMethod : containingClass.findMethodsByName(method.getName(), false)) {
|
||||
if (MethodSignatureUtil.areParametersErasureEqual(existingMethod, methodPrototype)) {
|
||||
updater.moveCaretTo(existingMethod.getTextOffset());
|
||||
updater.message(JavaBundle.message("default.param.value.warning",
|
||||
existingMethod.isConstructor() ? 0 : 1));
|
||||
String description = RefactoringUIUtil.getDescription(existingMethod, false);
|
||||
updater.message(StringUtil.capitalize(JavaBundle.message("default.param.value.warning", description)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
+13
-4
@@ -1,10 +1,8 @@
|
||||
// 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.light;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiRecordComponent;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
@@ -46,6 +44,17 @@ public class LightCompactConstructorParameter extends LightParameter implements
|
||||
return myRecordComponent.getNavigationElement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PsiElement findSameElementInCopy(@NotNull PsiFile copy) {
|
||||
PsiMethod constructor = (PsiMethod)getDeclarationScope();
|
||||
PsiMethod copyConstructor = PsiTreeUtil.findSameElementInCopy(constructor, copy);
|
||||
PsiParameterList parameterList = constructor.getParameterList();
|
||||
int index = parameterList.getParameterIndex(this);
|
||||
PsiParameter parameter = copyConstructor.getParameterList().getParameter(index);
|
||||
assert parameter != null;
|
||||
return parameter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
||||
+21
@@ -3,6 +3,7 @@ package com.intellij.psi.impl.light;
|
||||
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.ElementPresentationUtil;
|
||||
import com.intellij.psi.util.JavaPsiRecordUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.ui.IconManager;
|
||||
import com.intellij.ui.PlatformIcons;
|
||||
@@ -80,6 +81,12 @@ public class LightRecordCanonicalConstructor extends LightMethod implements Synt
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PsiElement findSameElementInCopy(@NotNull PsiFile copy) {
|
||||
PsiClass copyClass = PsiTreeUtil.findSameElementInCopy(myContainingClass, copy);
|
||||
return Objects.requireNonNull(JavaPsiRecordUtil.findCanonicalConstructor(copyClass));
|
||||
}
|
||||
|
||||
public static class LightRecordConstructorParameter extends LightParameterWrapper {
|
||||
private final @NotNull LightParameterListWrapper myWrapper;
|
||||
|
||||
@@ -137,6 +144,20 @@ public class LightRecordCanonicalConstructor extends LightMethod implements Synt
|
||||
return getParent().getContainingFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PsiElement findSameElementInCopy(@NotNull PsiFile copy) {
|
||||
PsiParameterList parameterList = (PsiParameterList)getParent();
|
||||
int index = parameterList.getParameterIndex(this);
|
||||
PsiClass recordClass = PsiTreeUtil.getParentOfType(this, PsiClass.class);
|
||||
PsiClass copyClass = PsiTreeUtil.findSameElementInCopy(recordClass, copy);
|
||||
assert copyClass != null;
|
||||
PsiMethod copyConstructor = JavaPsiRecordUtil.findCanonicalConstructor(copyClass);
|
||||
assert copyConstructor != null;
|
||||
PsiParameter copyParameter = copyConstructor.getParameterList().getParameter(index);
|
||||
assert copyParameter != null;
|
||||
return copyParameter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// "Generate overloaded constructor with default parameter values" "true"
|
||||
record Point(int x, int y) {
|
||||
Point() {
|
||||
this(0, 0);
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Generate overloaded constructor with default parameter values" "true"
|
||||
record Point(int x, int y) {
|
||||
Point() {
|
||||
this(0, 0);
|
||||
}
|
||||
|
||||
public Point {
|
||||
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// "Generate overloaded constructor with default parameter values" "true"
|
||||
record <caret>Point(int x, int y) {
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// "Generate overloaded constructor with default parameter values" "true"
|
||||
record<caret> Point(int x, int y) {
|
||||
public Point {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1519,7 +1519,7 @@ simplify.stream.inspection.iterate.take.while.fix.message=Can be replaced with t
|
||||
side.effects.pattern.message=<html>\n<body>\nThere are possible side effects found in {0}<br>\nYou can:\n<br>\n-\\ <b>Remove</b> variable usages along with all expressions involved, or<br>\n-\\ <b>Transform</b> expressions assigned to variable into the statements on their own.<br>\n<div style="padding-left: 0.6cm;">\n That is,<br>\n <table border="0">\n <tr>\n <td><code>{1};</code></td>\n </tr>\n </table>\n becomes: <br>\n <table border="0">\n <tr>\n <td><code>{2};</code></td>\n </tr>\n </table>\n</div>\n</body>\n</html>
|
||||
side.effects.expression.presentation=expression ''{0}''
|
||||
change.signature.from.usage.short.name=<html> Change signature of {0}({1})</html>
|
||||
default.param.value.warning={0, choice, 0#Constructor|1#Method} with the chosen signature already exists
|
||||
default.param.value.warning={0} already exists
|
||||
qualify.static.constant.access=Qualify static constant access
|
||||
qualify.static.access.command.name=Qualify Static Access
|
||||
qualify.static.call.fix.text=Qualify static call
|
||||
|
||||
Reference in New Issue
Block a user