diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtClassBody.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtClassBody.kt index ed17b4cb856..b85751495bf 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtClassBody.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtClassBody.kt @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes.* +import org.jetbrains.kotlin.utils.addToStdlib.lastIsInstanceOrNull import java.util.* class KtClassBody : KtElementImplStub>, KtDeclarationContainer { @@ -57,4 +58,22 @@ class KtClassBody : KtElementImplStub>, KtDec */ val danglingAnnotations: List get() = getStubOrPsiChildrenAsList(MODIFIER_LIST).flatMap { it.annotationEntries } + + private fun addEnumEntriesTerminator() { + val klass = parent as? KtClass ?: return + if (!klass.isEnum()) return + + val lastEntry = klass.declarations.lastIsInstanceOrNull() + if (KtPsiUtil.skipTrailingWhitespacesAndComments(lastEntry ?: firstChild)?.node?.elementType == KtTokens.SEMICOLON) return + super.addAfter(KtPsiFactory(this).createSemicolon(), lastEntry) + } + + override fun addAfter(element: PsiElement, anchor: PsiElement?): PsiElement? { + return super.addAfter(element, anchor) + .apply { + if (element !is KtEnumEntry) { + addEnumEntriesTerminator() + } + } + } } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt index 8d34bd88d45..bb3ab299398 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt @@ -865,7 +865,10 @@ fun ExtractionData.getDefaultVisibility(): String { if (!isVisibilityApplicable()) return "" val parent = targetSibling.getStrictParentOfType() - if (parent is KtClass && parent.isInterface()) return "" + if (parent is KtClass) { + if (parent.isInterface()) return "" + if (parent.isEnum() && commonParent.getNonStrictParentOfType()?.getStrictParentOfType() == parent) return "" + } return "private" } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractorUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractorUtil.kt index f26c5e58270..1ae54187d39 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractorUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractorUtil.kt @@ -591,18 +591,19 @@ fun ExtractionGeneratorConfiguration.generateDeclaration( return with(descriptor.extractionData) { val targetContainer = anchor.getParent()!! + // TODO: Get rid of explicit new-lines in favor of formatter rules val emptyLines = psiFactory.createWhiteSpace("\n\n") if (insertBefore) { - val declarationInFile = targetContainer.addBefore(declaration, anchor) as KtNamedDeclaration - targetContainer.addBefore(emptyLines, anchor) - - declarationInFile + (targetContainer.addBefore(declaration, anchor) as KtNamedDeclaration).apply { + targetContainer.addBefore(emptyLines, anchor) + } } else { - val declarationInFile = targetContainer.addAfter(declaration, anchor) as KtNamedDeclaration - targetContainer.addAfter(emptyLines, anchor) - - declarationInFile + (targetContainer.addAfter(declaration, anchor) as KtNamedDeclaration).apply { + if (!(targetContainer is KtClassBody && (targetContainer.parent as? KtClass)?.isEnum() ?: false)) { + targetContainer.addAfter(emptyLines, anchor) + } + } } } } @@ -610,8 +611,13 @@ fun ExtractionGeneratorConfiguration.generateDeclaration( val duplicates = if (generatorOptions.inTempFile) Collections.emptyList() else descriptor.duplicates val anchor = with(descriptor.extractionData) { + val targetParent = targetSibling.parent + val anchorCandidates = duplicates.mapTo(ArrayList()) { it.range.elements.first() } anchorCandidates.add(targetSibling) + if (targetSibling is KtEnumEntry) { + anchorCandidates.add(targetSibling.siblings().last { it is KtEnumEntry }) + } val marginalCandidate = if (insertBefore) { anchorCandidates.minBy { it.startOffset }!! @@ -621,8 +627,7 @@ fun ExtractionGeneratorConfiguration.generateDeclaration( } // Ascend to the level of targetSibling - val targetParent = targetSibling.getParent() - marginalCandidate.parentsWithSelf.first { it.getParent() == targetParent } + marginalCandidate.parentsWithSelf.first { it.parent == targetParent } } val shouldInsert = !(generatorOptions.inTempFile || generatorOptions.target == ExtractionTarget.FAKE_LAMBDALIKE_FUNCTION) diff --git a/idea/testData/refactoring/extractFunction/basic/extractToEnumClassBody.kt b/idea/testData/refactoring/extractFunction/basic/extractToEnumClassBody.kt new file mode 100644 index 00000000000..0971360d4ae --- /dev/null +++ b/idea/testData/refactoring/extractFunction/basic/extractToEnumClassBody.kt @@ -0,0 +1,10 @@ +// PARAM_DESCRIPTOR: value-parameter val n: kotlin.Int defined in E.A.foo +// PARAM_TYPES: kotlin.Int +enum class E { + // SIBLING: + A { + fun foo(n: Int) = n + 1 + }, + B, + C +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/basic/extractToEnumClassBody.kt.after b/idea/testData/refactoring/extractFunction/basic/extractToEnumClassBody.kt.after new file mode 100644 index 00000000000..352f2bab93d --- /dev/null +++ b/idea/testData/refactoring/extractFunction/basic/extractToEnumClassBody.kt.after @@ -0,0 +1,12 @@ +// PARAM_DESCRIPTOR: value-parameter val n: kotlin.Int defined in E.A.foo +// PARAM_TYPES: kotlin.Int +enum class E { + // SIBLING: + A { + fun foo(n: Int) = i(n) + }, + B, + C; + + fun i(n: Int) = n + 1 +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/JetExtractionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/JetExtractionTestGenerated.java index 74ec28c9f81..e8e5df11aa6 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/JetExtractionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/JetExtractionTestGenerated.java @@ -510,6 +510,12 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest { doExtractFunctionTest(fileName); } + @TestMetadata("extractToEnumClassBody.kt") + public void testExtractToEnumClassBody() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/basic/extractToEnumClassBody.kt"); + doExtractFunctionTest(fileName); + } + @TestMetadata("fakeOverride.kt") public void testFakeOverride() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/basic/fakeOverride.kt"); diff --git a/j2k/testData/fileOrElement/settings/openByDefault.kt b/j2k/testData/fileOrElement/settings/openByDefault.kt index f20aaa05214..ceb0c110edc 100644 --- a/j2k/testData/fileOrElement/settings/openByDefault.kt +++ b/j2k/testData/fileOrElement/settings/openByDefault.kt @@ -31,6 +31,7 @@ internal open class D : I { internal enum class E { ; + fun foo(): Int { return 0 }