Extraction Engine: When extracting to enum class body, place new declaration after the last entry
#KT-9629 Fixed
This commit is contained in:
committed by
Alexey Sedunov
parent
40f2a983cc
commit
3fd6864461
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.lexer.KtTokens
|
|||||||
import org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub
|
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.psi.stubs.elements.KtStubElementTypes.*
|
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes.*
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.lastIsInstanceOrNull
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class KtClassBody : KtElementImplStub<KotlinPlaceHolderStub<KtClassBody>>, KtDeclarationContainer {
|
class KtClassBody : KtElementImplStub<KotlinPlaceHolderStub<KtClassBody>>, KtDeclarationContainer {
|
||||||
@@ -57,4 +58,22 @@ class KtClassBody : KtElementImplStub<KotlinPlaceHolderStub<KtClassBody>>, KtDec
|
|||||||
*/
|
*/
|
||||||
val danglingAnnotations: List<KtAnnotationEntry>
|
val danglingAnnotations: List<KtAnnotationEntry>
|
||||||
get() = getStubOrPsiChildrenAsList(MODIFIER_LIST).flatMap { it.annotationEntries }
|
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<KtEnumEntry>()
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-1
@@ -865,7 +865,10 @@ fun ExtractionData.getDefaultVisibility(): String {
|
|||||||
if (!isVisibilityApplicable()) return ""
|
if (!isVisibilityApplicable()) return ""
|
||||||
|
|
||||||
val parent = targetSibling.getStrictParentOfType<KtDeclaration>()
|
val parent = targetSibling.getStrictParentOfType<KtDeclaration>()
|
||||||
if (parent is KtClass && parent.isInterface()) return ""
|
if (parent is KtClass) {
|
||||||
|
if (parent.isInterface()) return ""
|
||||||
|
if (parent.isEnum() && commonParent.getNonStrictParentOfType<KtEnumEntry>()?.getStrictParentOfType<KtClass>() == parent) return ""
|
||||||
|
}
|
||||||
|
|
||||||
return "private"
|
return "private"
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-10
@@ -591,18 +591,19 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
|||||||
|
|
||||||
return with(descriptor.extractionData) {
|
return with(descriptor.extractionData) {
|
||||||
val targetContainer = anchor.getParent()!!
|
val targetContainer = anchor.getParent()!!
|
||||||
|
// TODO: Get rid of explicit new-lines in favor of formatter rules
|
||||||
val emptyLines = psiFactory.createWhiteSpace("\n\n")
|
val emptyLines = psiFactory.createWhiteSpace("\n\n")
|
||||||
if (insertBefore) {
|
if (insertBefore) {
|
||||||
val declarationInFile = targetContainer.addBefore(declaration, anchor) as KtNamedDeclaration
|
(targetContainer.addBefore(declaration, anchor) as KtNamedDeclaration).apply {
|
||||||
targetContainer.addBefore(emptyLines, anchor)
|
targetContainer.addBefore(emptyLines, anchor)
|
||||||
|
}
|
||||||
declarationInFile
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val declarationInFile = targetContainer.addAfter(declaration, anchor) as KtNamedDeclaration
|
(targetContainer.addAfter(declaration, anchor) as KtNamedDeclaration).apply {
|
||||||
targetContainer.addAfter(emptyLines, anchor)
|
if (!(targetContainer is KtClassBody && (targetContainer.parent as? KtClass)?.isEnum() ?: false)) {
|
||||||
|
targetContainer.addAfter(emptyLines, anchor)
|
||||||
declarationInFile
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -610,8 +611,13 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
|||||||
val duplicates = if (generatorOptions.inTempFile) Collections.emptyList() else descriptor.duplicates
|
val duplicates = if (generatorOptions.inTempFile) Collections.emptyList() else descriptor.duplicates
|
||||||
|
|
||||||
val anchor = with(descriptor.extractionData) {
|
val anchor = with(descriptor.extractionData) {
|
||||||
|
val targetParent = targetSibling.parent
|
||||||
|
|
||||||
val anchorCandidates = duplicates.mapTo(ArrayList<PsiElement>()) { it.range.elements.first() }
|
val anchorCandidates = duplicates.mapTo(ArrayList<PsiElement>()) { it.range.elements.first() }
|
||||||
anchorCandidates.add(targetSibling)
|
anchorCandidates.add(targetSibling)
|
||||||
|
if (targetSibling is KtEnumEntry) {
|
||||||
|
anchorCandidates.add(targetSibling.siblings().last { it is KtEnumEntry })
|
||||||
|
}
|
||||||
|
|
||||||
val marginalCandidate = if (insertBefore) {
|
val marginalCandidate = if (insertBefore) {
|
||||||
anchorCandidates.minBy { it.startOffset }!!
|
anchorCandidates.minBy { it.startOffset }!!
|
||||||
@@ -621,8 +627,7 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ascend to the level of targetSibling
|
// Ascend to the level of targetSibling
|
||||||
val targetParent = targetSibling.getParent()
|
marginalCandidate.parentsWithSelf.first { it.parent == targetParent }
|
||||||
marginalCandidate.parentsWithSelf.first { it.getParent() == targetParent }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val shouldInsert = !(generatorOptions.inTempFile || generatorOptions.target == ExtractionTarget.FAKE_LAMBDALIKE_FUNCTION)
|
val shouldInsert = !(generatorOptions.inTempFile || generatorOptions.target == ExtractionTarget.FAKE_LAMBDALIKE_FUNCTION)
|
||||||
|
|||||||
@@ -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) = <selection>n + 1</selection>
|
||||||
|
},
|
||||||
|
B,
|
||||||
|
C
|
||||||
|
}
|
||||||
+12
@@ -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
|
||||||
|
}
|
||||||
+6
@@ -510,6 +510,12 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
|
|||||||
doExtractFunctionTest(fileName);
|
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")
|
@TestMetadata("fakeOverride.kt")
|
||||||
public void testFakeOverride() throws Exception {
|
public void testFakeOverride() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/basic/fakeOverride.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/basic/fakeOverride.kt");
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ internal open class D : I {
|
|||||||
|
|
||||||
internal enum class E {
|
internal enum class E {
|
||||||
;
|
;
|
||||||
|
|
||||||
fun foo(): Int {
|
fun foo(): Int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user