[java] add move all members to class fix

GitOrigin-RevId: f5ad44f993f2f386c9847dc57ddac86cb4cf45d2
This commit is contained in:
Roman Ivanov
2023-09-07 08:56:35 +02:00
committed by intellij-monorepo-bot
parent 96dbed77fc
commit 31928210fc
12 changed files with 219 additions and 2 deletions

View File

@@ -658,3 +658,6 @@ error.unnamed.method.parameter.not.allowed=Unnamed method parameter is not allow
error.unnamed.variable.not.allowed.in.this.context=Unnamed variable declaration is not allowed in this context
error.unnamed.variable.brackets=Brackets are not allowed after unnamed variable declaration
error.unnamed.variable.without.initializer=Unnamed variable declaration must have an initializer
intention.family.name.move.members.into.class=Move members into class
chooser.popup.title.select.class.to.move.members.to=Select Target Class
intention.family.name.move.members.to=Move members to {0}

View File

@@ -3730,6 +3730,7 @@ public final class HighlightUtil {
return feature.level;
}
@Nullable
static HighlightInfo.Builder checkFeature(@NotNull PsiElement element,
@NotNull HighlightingFeature feature,
@NotNull LanguageLevel level,
@@ -3737,6 +3738,7 @@ public final class HighlightUtil {
return checkFeature(element, feature, level, file, null, HighlightInfoType.ERROR);
}
@Nullable
static HighlightInfo.Builder checkFeature(@NotNull PsiElement element,
@NotNull HighlightingFeature feature,
@NotNull LanguageLevel level,

View File

@@ -6,6 +6,7 @@ import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.daemon.JavaErrorBundle;
import com.intellij.codeInsight.daemon.impl.*;
import com.intellij.codeInsight.daemon.impl.quickfix.AdjustFunctionContextFix;
import com.intellij.codeInsight.daemon.impl.quickfix.MoveMembersIntoClassFix;
import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
@@ -2219,12 +2220,25 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
}
private HighlightInfo.Builder checkUnnamedClassMember(@NotNull PsiMember member) {
if (!(member.getContainingClass() instanceof PsiUnnamedClass)) {
if (!(member.getContainingClass() instanceof PsiUnnamedClass unnamedClass)) {
return null;
}
return checkFeature(member, HighlightingFeature.UNNAMED_CLASSES);
HighlightInfo.Builder builder = checkFeature(member, HighlightingFeature.UNNAMED_CLASSES);
if (builder == null) return null;
if (!(member instanceof PsiClass) && !HighlightingFeature.UNNAMED_CLASSES.isAvailable(member)) {
boolean hasClassToRelocate = PsiTreeUtil.findChildOfType(unnamedClass, PsiClass.class) != null;
if (hasClassToRelocate) {
MoveMembersIntoClassFix fix = new MoveMembersIntoClassFix(unnamedClass);
builder.registerFix(fix, null, null, null, null);
}
}
return builder;
}
@Nullable
private HighlightInfo.Builder checkFeature(@NotNull PsiElement element, @NotNull HighlightingFeature feature) {
return HighlightUtil.checkFeature(element, feature, myLanguageLevel, myFile);
}

View File

@@ -0,0 +1,69 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.java.analysis.JavaAnalysisBundle;
import com.intellij.modcommand.*;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
public class MoveMembersIntoClassFix implements ModCommandAction {
private final SmartPsiElementPointer<PsiUnnamedClass> myUnnamedClass;
public MoveMembersIntoClassFix(PsiUnnamedClass unnamedClass) {
myUnnamedClass = SmartPointerManager.createPointer(unnamedClass);
}
@Override
public @NotNull String getFamilyName() {
return JavaAnalysisBundle.message("intention.family.name.move.members.into.class");
}
@Override
public @Nullable Presentation getPresentation(@NotNull ActionContext context) {
return Presentation.of(JavaAnalysisBundle.message("intention.family.name.move.members.into.class"));
}
@Override
public @NotNull ModCommand perform(@NotNull ActionContext context) {
PsiUnnamedClass unnamedClass = myUnnamedClass.getElement();
if (unnamedClass == null) return ModCommand.nop();
PsiClass[] innerClasses = unnamedClass.getInnerClasses();
List<? extends ModCommandAction> actionsPerClass = Arrays.stream(innerClasses).map(MoveAllMembersToParticularClassAction::new).toList();
return new ModChooseAction(JavaAnalysisBundle.message("chooser.popup.title.select.class.to.move.members.to"), actionsPerClass);
}
private static class MoveAllMembersToParticularClassAction extends PsiUpdateModCommandAction<PsiClass> {
private final @NonNls String className;
private MoveAllMembersToParticularClassAction(PsiClass cls) {
super(cls);
className = cls.getName();
}
@Override
protected void invoke(@NotNull ActionContext context, @NotNull PsiClass element, @NotNull ModPsiUpdater updater) {
if (!(element.getContainingClass() instanceof PsiUnnamedClass unn)) return;
PsiMember[] members = PsiTreeUtil.getChildrenOfType(unn, PsiMember.class);
if (members == null) return;
List<PsiMember> membersWithoutClasses = Arrays.stream(members).filter(member -> !(member instanceof PsiClass)).toList();
for (PsiMember member : membersWithoutClasses) {
PsiElement copyMember = member.copy();
member.delete();
element.add(copyMember);
}
}
@Override
public @NotNull String getFamilyName() {
return JavaAnalysisBundle.message("intention.family.name.move.members.to", className);
}
}
}

View File

@@ -0,0 +1,7 @@
// "Move members into class" "true-preview"
class A {
int field = 12;
}

View File

@@ -0,0 +1,9 @@
// "Move members into class" "true-preview"
class A {
void foo() {
}
}

View File

@@ -0,0 +1,20 @@
// "Move members into class" "true-preview"
class A {
static {
System.out.println("initializer");
}
int x = 21;
String s = "asdsad";
void foo() {
}
void bar() {
}
}

View File

@@ -0,0 +1,8 @@
// "Move members into class" "true-preview"
int field<caret> = 12;
class A {
}

View File

@@ -0,0 +1,10 @@
// "Move members into class" "true-preview"
void foo<caret>() {
}
class A {
}

View File

@@ -0,0 +1,22 @@
// "Move members into class" "true-preview"
void foo<caret>() {
}
void bar<caret>() {
}
int x = 21;
String s = "asdsad";
static {
System.out.println("initializer");
}
class A {
}

View File

@@ -0,0 +1,18 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
import com.intellij.pom.java.LanguageLevel;
public class MoveMembersIntoClassFixTest extends LightQuickFixParameterizedTestCase {
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/moveMembersIntoClass";
}
@Override
protected LanguageLevel getLanguageLevel() {
return LanguageLevel.JDK_16;
}
}

View File

@@ -0,0 +1,35 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.codeInsight.daemon
import com.intellij.JavaTestUtil
import com.intellij.pom.java.LanguageLevel
import com.intellij.testFramework.IdeaTestUtil
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase
class MoveMembersFixTest : LightJavaCodeInsightFixtureTestCase() {
override fun getProjectDescriptor() = JAVA_21
override fun getBasePath() = JavaTestUtil.getRelativeJavaTestDataPath() + "/codeInsight/daemonCodeAnalyzer/unnamedClass"
fun testHighlightInsufficientLevel() {
IdeaTestUtil.withLevel(module, LanguageLevel.JDK_20, Runnable {
doTest()
})
}
fun testWithPackageStatement() {
doTest()
}
fun testStaticInitializer() {
doTest()
}
fun testHashCodeInMethod() {
doTest()
}
private fun doTest() {
myFixture.configureByFile(getTestName(false) + ".java")
myFixture.checkHighlighting()
}
}