diff --git a/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java b/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java index 0a0fd385140b..9464c9f2b8bb 100644 --- a/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java +++ b/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java @@ -485,4 +485,6 @@ public abstract class QuickFixFactory { */ @NotNull public abstract IntentionAction createAddToPermitsListFix(@NotNull PsiClass subClass, @NotNull PsiClass superClass); + + public abstract IntentionAction createMoveClassToPackageFix(@NotNull PsiClass classToMove, @NotNull String packageName); } \ No newline at end of file diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java index 50490a820e16..265c0fa7848f 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java @@ -1094,10 +1094,16 @@ public final class HighlightClassUtil { } else { if (currentModule == null && !psiFacade.arePackagesTheSame(aClass, inheritorClass)) { - holder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) - .range(permitted) - .descriptionAndTooltip(JavaErrorBundle.message("class.not.allowed.to.extend.sealed.class.from.another.package")) - .create()); + HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) + .range(permitted) + .descriptionAndTooltip(JavaErrorBundle.message("class.not.allowed.to.extend.sealed.class.from.another.package")) + .create(); + PsiFile parentFile = aClass.getContainingFile(); + if (parentFile instanceof PsiClassOwner) { + String parentPackage = ((PsiClassOwner)parentFile).getPackageName(); + QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createMoveClassToPackageFix(inheritorClass, parentPackage)); + } + holder.add(info); } else if (currentModule != null && currentModule != JavaModuleGraphUtil.findDescriptorByElement(inheritorClass)) { holder.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java index 309140da8c95..5d8b308364de 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java @@ -941,4 +941,9 @@ public final class QuickFixFactoryImpl extends QuickFixFactory { @NotNull PsiClass superClass) { return new AddToPermitsListFix(subClass, superClass); } + + @Override + public IntentionAction createMoveClassToPackageFix(@NotNull PsiClass classToMove, @NotNull String packageName) { + return new MoveToPackageFix(classToMove.getContainingFile(), packageName); + } } diff --git a/java/java-impl/src/com/intellij/codeInspection/MoveToPackageFix.java b/java/java-impl/src/com/intellij/codeInspection/MoveToPackageFix.java index 11621e33e9ba..89be9297973b 100644 --- a/java/java-impl/src/com/intellij/codeInspection/MoveToPackageFix.java +++ b/java/java-impl/src/com/intellij/codeInspection/MoveToPackageFix.java @@ -18,7 +18,9 @@ package com.intellij.codeInspection; import com.intellij.CommonBundle; import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInsight.daemon.QuickFixBundle; +import com.intellij.codeInspection.util.IntentionName; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.psi.*; @@ -29,18 +31,19 @@ import com.intellij.refactoring.move.moveClassesOrPackages.SingleSourceRootMoveD import com.intellij.refactoring.util.RefactoringMessageUtil; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -public class MoveToPackageFix implements LocalQuickFix { +public class MoveToPackageFix extends LocalQuickFixAndIntentionActionOnPsiElement { private static final Logger LOG = Logger.getInstance(MoveToPackageFix.class); private final String myTargetPackage; - public MoveToPackageFix(String targetPackage) { + public MoveToPackageFix(PsiFile psiFile, String targetPackage) { + super(psiFile); myTargetPackage = targetPackage; } @Override - @NotNull - public String getName() { + public @IntentionName @NotNull String getText() { return QuickFixBundle.message("move.class.to.package.text", myTargetPackage); } @@ -60,15 +63,12 @@ public class MoveToPackageFix implements LocalQuickFix { } @Override - public boolean startInWriteAction() { - return false; - } - - @Override - public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) { - PsiElement element = descriptor.getPsiElement(); - if (element == null) return; - final PsiFile myFile = element.getContainingFile(); + public void invoke(@NotNull Project project, + @NotNull PsiFile file, + @Nullable Editor editor, + @NotNull PsiElement startElement, + @NotNull PsiElement endElement) { + final PsiFile myFile = startElement.getContainingFile(); if (!FileModificationService.getInstance().prepareFileForWrite(myFile)) return; @@ -103,4 +103,9 @@ public class MoveToPackageFix implements LocalQuickFix { LOG.error(e); } } + + @Override + public boolean startInWriteAction() { + return false; + } } diff --git a/java/java-impl/src/com/intellij/codeInspection/wrongPackageStatement/WrongPackageStatementInspection.java b/java/java-impl/src/com/intellij/codeInspection/wrongPackageStatement/WrongPackageStatementInspection.java index 540ca28d8116..d04432895c96 100644 --- a/java/java-impl/src/com/intellij/codeInspection/wrongPackageStatement/WrongPackageStatementInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/wrongPackageStatement/WrongPackageStatementInspection.java @@ -33,7 +33,7 @@ import java.util.List; public class WrongPackageStatementInspection extends AbstractBaseJavaLocalInspectionTool { protected void addMoveToPackageFix(PsiFile file, String packName, List availableFixes) { - MoveToPackageFix moveToPackageFix = new MoveToPackageFix(packName); + MoveToPackageFix moveToPackageFix = new MoveToPackageFix(file, packName); if (moveToPackageFix.isAvailable(file)) { availableFixes.add(moveToPackageFix); } diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/MoveToPackageTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/MoveToPackageTest.java new file mode 100644 index 000000000000..e4e0e2aa5be6 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/MoveToPackageTest.java @@ -0,0 +1,39 @@ +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.java.codeInsight.daemon.quickFix; + +import com.intellij.psi.PsiClass; +import com.intellij.testFramework.LightProjectDescriptor; +import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase; +import org.jetbrains.annotations.NotNull; + +public class MoveToPackageTest extends LightJavaCodeInsightFixtureTestCase { + @Override + protected @NotNull LightProjectDescriptor getProjectDescriptor() { + return LightJavaCodeInsightFixtureTestCase.JAVA_15; + } + + public void testSimple() { + PsiClass bClass = myFixture.addClass("package foo;\nimport bar.A;\npublic final class B extends A {}"); + myFixture.configureByText("A.java", "package bar;\n import foo.B;\npublic sealed class A permits B {}"); + invokeFix("Move to package 'bar'"); + assertEquals("package bar;\nimport bar.A;\npublic final class B extends A {}", bClass.getContainingFile().getText()); + } + + public void testNestedClass() { + PsiClass bClass = myFixture.addClass("package foo;\nimport bar.A;\npublic class B { public static final class C extends A {} }"); + myFixture.configureByText("A.java", "package bar;\n import foo.B;\npublic sealed class A permits B.C {}"); + invokeFix("Move to package 'bar'"); + assertEquals("package bar;\nimport bar.A;\npublic class B { public static final class C extends A {} }", + bClass.getContainingFile().getText()); + } + + public void testNonAccessibleClass() { + myFixture.addClass("package foo;\nimport bar.A;\nfinal class B extends A {}"); + myFixture.configureByText("A.java", "package bar;\n import foo.B;\npublic sealed class A permits B {}"); + assertEmpty(myFixture.filterAvailableIntentions("Move to package 'bar'")); + } + + private void invokeFix(@NotNull String hint) { + myFixture.launchAction(myFixture.findSingleIntention(hint)); + } +}