Create from Usage: Add commas after enum entries
#KT-10800 Fixed
This commit is contained in:
@@ -142,6 +142,8 @@ fun PsiElement.getNextSiblingIgnoringWhitespaceAndComments(): PsiElement? {
|
|||||||
|
|
||||||
inline fun <reified T : PsiElement> T.nextSiblingOfSameType() = PsiTreeUtil.getNextSiblingOfType(this, T::class.java)
|
inline fun <reified T : PsiElement> T.nextSiblingOfSameType() = PsiTreeUtil.getNextSiblingOfType(this, T::class.java)
|
||||||
|
|
||||||
|
inline fun <reified T : PsiElement> T.prevSiblingOfSameType() = PsiTreeUtil.getPrevSiblingOfType(this, T::class.java)
|
||||||
|
|
||||||
fun PsiElement?.isAncestor(element: PsiElement, strict: Boolean = false): Boolean {
|
fun PsiElement?.isAncestor(element: PsiElement, strict: Boolean = false): Boolean {
|
||||||
return PsiTreeUtil.isAncestor(this, element, strict)
|
return PsiTreeUtil.isAncestor(this, element, strict)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ fun <T : KtDeclaration> insertMembersAfter(
|
|||||||
else if (bound == null && body.declarations.isNotEmpty()) {
|
else if (bound == null && body.declarations.isNotEmpty()) {
|
||||||
afterAnchor = body.lBrace!!
|
afterAnchor = body.lBrace!!
|
||||||
}
|
}
|
||||||
else if (bound != null && afterAnchor.startOffset >= bound.startOffset) {
|
else if (bound != null && afterAnchor.startOffset > bound.startOffset) {
|
||||||
afterAnchor = bound.prevSibling!!
|
afterAnchor = bound.prevSibling!!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,6 +207,6 @@ fun <T : KtDeclaration> insertMembersAfter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : KtDeclaration> insertMember(editor: Editor, classOrObject: KtClassOrObject, declaration: T): T {
|
fun <T : KtDeclaration> insertMember(editor: Editor?, classOrObject: KtClassOrObject, declaration: T, anchor: PsiElement? = null): T {
|
||||||
return insertMembersAfter(editor, classOrObject, listOf(declaration)).single()
|
return insertMembersAfter(editor, classOrObject, listOf(declaration), anchor).single()
|
||||||
}
|
}
|
||||||
@@ -18,29 +18,55 @@ package org.jetbrains.kotlin.idea.formatter
|
|||||||
|
|
||||||
import com.intellij.lang.ASTNode
|
import com.intellij.lang.ASTNode
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.impl.source.codeStyle.PreFormatProcessor
|
import com.intellij.psi.impl.source.codeStyle.PreFormatProcessor
|
||||||
|
import com.intellij.psi.tree.IElementType
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.allChildren
|
import org.jetbrains.kotlin.psi.psiUtil.allChildren
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.nextSiblingOfSameType
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.prevSiblingOfSameType
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.lastIsInstanceOrNull
|
import org.jetbrains.kotlin.utils.addToStdlib.lastIsInstanceOrNull
|
||||||
|
|
||||||
class KotlinPreFormatProcessor : PreFormatProcessor {
|
class KotlinPreFormatProcessor : PreFormatProcessor {
|
||||||
class Visitor(var range: TextRange) : KtTreeVisitorVoid() {
|
class Visitor(var range: TextRange) : KtTreeVisitorVoid() {
|
||||||
|
private fun PsiElement.containsToken(type: IElementType) = allChildren.any { it.node.elementType == type }
|
||||||
|
|
||||||
override fun visitNamedDeclaration(declaration: KtNamedDeclaration) {
|
override fun visitNamedDeclaration(declaration: KtNamedDeclaration) {
|
||||||
if (!range.contains(declaration.textRange)) return
|
if (!range.contains(declaration.textRange)) return
|
||||||
|
|
||||||
if (declaration is KtEnumEntry) return
|
|
||||||
val classBody = declaration.parent as? KtClassBody ?: return
|
val classBody = declaration.parent as? KtClassBody ?: return
|
||||||
val klass = classBody.parent as? KtClass ?: return
|
val klass = classBody.parent as? KtClass ?: return
|
||||||
if (!klass.isEnum()) return
|
if (!klass.isEnum()) return
|
||||||
|
|
||||||
val lastEntry = klass.declarations.lastIsInstanceOrNull<KtEnumEntry>()
|
var delta = 0
|
||||||
if (lastEntry != null && lastEntry.allChildren.any { it.node.elementType == KtTokens.SEMICOLON }) return
|
|
||||||
if (lastEntry == null && classBody.allChildren.any { it.node.elementType == KtTokens.SEMICOLON }) return
|
|
||||||
|
|
||||||
val semicolon = KtPsiFactory(klass).createSemicolon()
|
if (declaration is KtEnumEntry) {
|
||||||
classBody.addAfter(semicolon, lastEntry)
|
val comma = KtPsiFactory(klass).createComma()
|
||||||
range = TextRange(range.startOffset, range.endOffset + semicolon.textLength)
|
|
||||||
|
declaration.nextSiblingOfSameType()?.let { nextEntry ->
|
||||||
|
if (declaration.containsToken(KtTokens.COMMA)) return@let
|
||||||
|
classBody.addAfter(comma, declaration)
|
||||||
|
delta += comma.textLength
|
||||||
|
}
|
||||||
|
|
||||||
|
declaration.prevSiblingOfSameType()?.let { prevEntry ->
|
||||||
|
if (prevEntry.containsToken(KtTokens.COMMA)) return@let
|
||||||
|
classBody.addAfter(comma, prevEntry)
|
||||||
|
delta += comma.textLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val lastEntry = klass.declarations.lastIsInstanceOrNull<KtEnumEntry>()
|
||||||
|
if (lastEntry != null && lastEntry.containsToken(KtTokens.SEMICOLON)) return
|
||||||
|
if (lastEntry == null && classBody.containsToken(KtTokens.SEMICOLON)) return
|
||||||
|
|
||||||
|
val semicolon = KtPsiFactory(klass).createSemicolon()
|
||||||
|
classBody.addAfter(semicolon, lastEntry)
|
||||||
|
delta += semicolon.textLength
|
||||||
|
}
|
||||||
|
|
||||||
|
range = TextRange(range.startOffset, range.endOffset + delta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -48,6 +48,7 @@ import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
|
|||||||
import org.jetbrains.kotlin.idea.core.CollectingNameValidator
|
import org.jetbrains.kotlin.idea.core.CollectingNameValidator
|
||||||
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
||||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.ClassKind
|
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.ClassKind
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.insertMember
|
||||||
import org.jetbrains.kotlin.idea.refactoring.*
|
import org.jetbrains.kotlin.idea.refactoring.*
|
||||||
import org.jetbrains.kotlin.idea.util.DialogWithEditor
|
import org.jetbrains.kotlin.idea.util.DialogWithEditor
|
||||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||||
@@ -576,7 +577,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
containingElement is KtClassOrObject -> {
|
containingElement is KtClassOrObject -> {
|
||||||
addDeclarationToClassOrObject(containingElement, declaration)
|
insertMember(null, containingElement, declaration, containingElement.declarations.lastOrNull())
|
||||||
}
|
}
|
||||||
else -> throw AssertionError("Invalid containing element: ${containingElement.text}")
|
else -> throw AssertionError("Invalid containing element: ${containingElement.text}")
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+8
@@ -0,0 +1,8 @@
|
|||||||
|
// "Create enum constant 'PUBLIC'" "true"
|
||||||
|
enum class TopicState {
|
||||||
|
PRIVATE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
TopicState.<caret>PUBLIC
|
||||||
|
}
|
||||||
Vendored
+10
@@ -0,0 +1,10 @@
|
|||||||
|
// "Create enum constant 'PUBLIC'" "true"
|
||||||
|
enum class TopicState {
|
||||||
|
PRIVATE,
|
||||||
|
|
||||||
|
<caret>PUBLIC
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
TopicState.PUBLIC
|
||||||
|
}
|
||||||
Vendored
+8
@@ -0,0 +1,8 @@
|
|||||||
|
// "Create enum constant 'PUBLIC'" "true"
|
||||||
|
enum class TopicState {
|
||||||
|
PRIVATE,
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
TopicState.<caret>PUBLIC
|
||||||
|
}
|
||||||
Vendored
+10
@@ -0,0 +1,10 @@
|
|||||||
|
// "Create enum constant 'PUBLIC'" "true"
|
||||||
|
enum class TopicState {
|
||||||
|
PRIVATE,
|
||||||
|
|
||||||
|
<caret>PUBLIC
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
TopicState.PUBLIC
|
||||||
|
}
|
||||||
@@ -1448,6 +1448,18 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("enumEntryAddComma.kt")
|
||||||
|
public void testEnumEntryAddComma() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/referenceExpression/enumEntryAddComma.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("enumEntryHasComma.kt")
|
||||||
|
public void testEnumEntryHasComma() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/referenceExpression/enumEntryHasComma.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("enumEntryNoReceiver.kt")
|
@TestMetadata("enumEntryNoReceiver.kt")
|
||||||
public void testEnumEntryNoReceiver() throws Exception {
|
public void testEnumEntryNoReceiver() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/referenceExpression/enumEntryNoReceiver.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/referenceExpression/enumEntryNoReceiver.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user