Implement Abstract Member Intention: Support primary constructor parameters
#KT-8427 Fixed
This commit is contained in:
@@ -88,3 +88,19 @@ public open class KtClass : KtClassOrObject {
|
|||||||
|
|
||||||
public fun getClassOrInterfaceKeyword(): PsiElement? = findChildByType(TokenSet.create(KtTokens.CLASS_KEYWORD, KtTokens.INTERFACE_KEYWORD))
|
public fun getClassOrInterfaceKeyword(): PsiElement? = findChildByType(TokenSet.create(KtTokens.CLASS_KEYWORD, KtTokens.INTERFACE_KEYWORD))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun KtClass.createPrimaryConstructorIfAbsent(): KtPrimaryConstructor {
|
||||||
|
val constructor = getPrimaryConstructor()
|
||||||
|
if (constructor != null) return constructor
|
||||||
|
var anchor: PsiElement? = typeParameterList
|
||||||
|
if (anchor == null) anchor = nameIdentifier
|
||||||
|
if (anchor == null) anchor = lastChild
|
||||||
|
return addAfter(KtPsiFactory(project).createPrimaryConstructor(), anchor) as KtPrimaryConstructor
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun KtClass.createPrimaryConstructorParameterListIfAbsent(): KtParameterList {
|
||||||
|
val constructor = createPrimaryConstructorIfAbsent()
|
||||||
|
val parameterList = constructor.valueParameterList
|
||||||
|
if (parameterList != null) return parameterList
|
||||||
|
return constructor.add(KtPsiFactory(project).createParameterList("()")) as KtParameterList
|
||||||
|
}
|
||||||
@@ -153,29 +153,42 @@ public fun <T : KtDeclaration> insertMembersAfter(
|
|||||||
): List<T> {
|
): List<T> {
|
||||||
members.ifEmpty { return emptyList() }
|
members.ifEmpty { return emptyList() }
|
||||||
|
|
||||||
return runWriteAction<List<T>> {
|
return runWriteAction {
|
||||||
val body = classOrObject.getOrCreateBody()
|
val insertedMembers = SmartList<T>()
|
||||||
|
|
||||||
var afterAnchor = anchor ?: findInsertAfterAnchor(editor, body) ?: return@runWriteAction emptyList()
|
val (parameters, otherMembers) = members.partition { it is KtParameter }
|
||||||
val insertedMembers = members.mapTo(SmartList<T>()) {
|
|
||||||
if (classOrObject is KtClass && classOrObject.isEnum()) {
|
parameters.mapNotNullTo(insertedMembers) {
|
||||||
val enumEntries = classOrObject.declarations.filterIsInstance<KtEnumEntry>()
|
if (classOrObject !is KtClass) return@mapNotNullTo null
|
||||||
val bound = (enumEntries.lastOrNull() ?: classOrObject.allChildren.firstOrNull { it.node.elementType == KtTokens.SEMICOLON })
|
|
||||||
if (it !is KtEnumEntry) {
|
|
||||||
if (bound != null && afterAnchor.startOffset <= bound.startOffset) {
|
|
||||||
afterAnchor = bound
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (bound == null && body.declarations.isNotEmpty()) {
|
|
||||||
afterAnchor = body.lBrace!!
|
|
||||||
}
|
|
||||||
else if (bound != null && afterAnchor.startOffset >= bound.startOffset) {
|
|
||||||
afterAnchor = bound.prevSibling!!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
(body.addAfter(it, afterAnchor) as T).apply { afterAnchor = this }
|
(classOrObject.createPrimaryConstructorParameterListIfAbsent().addParameter(it as KtParameter) as T)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (otherMembers.isNotEmpty()) {
|
||||||
|
val body = classOrObject.getOrCreateBody()
|
||||||
|
|
||||||
|
var afterAnchor = anchor ?: findInsertAfterAnchor(editor, body) ?: return@runWriteAction emptyList()
|
||||||
|
otherMembers.mapNotNullTo(insertedMembers) {
|
||||||
|
if (classOrObject is KtClass && classOrObject.isEnum()) {
|
||||||
|
val enumEntries = classOrObject.declarations.filterIsInstance<KtEnumEntry>()
|
||||||
|
val bound = (enumEntries.lastOrNull() ?: classOrObject.allChildren.firstOrNull { it.node.elementType == KtTokens.SEMICOLON })
|
||||||
|
if (it !is KtEnumEntry) {
|
||||||
|
if (bound != null && afterAnchor.startOffset <= bound.startOffset) {
|
||||||
|
afterAnchor = bound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (bound == null && body.declarations.isNotEmpty()) {
|
||||||
|
afterAnchor = body.lBrace!!
|
||||||
|
}
|
||||||
|
else if (bound != null && afterAnchor.startOffset >= bound.startOffset) {
|
||||||
|
afterAnchor = bound.prevSibling!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
(body.addAfter(it, afterAnchor) as T).apply { afterAnchor = this }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ShortenReferences.DEFAULT.process(insertedMembers)
|
ShortenReferences.DEFAULT.process(insertedMembers)
|
||||||
|
|||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
interface A {
|
||||||
|
val foo: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
class B(<spot>override val foo: Int</spot>) : A {
|
||||||
|
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
interface A {
|
||||||
|
<spot>val foo: Int</spot>
|
||||||
|
}
|
||||||
|
|
||||||
|
class B : A {
|
||||||
|
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
This intention searches for all classes that can implement selected abstract property, and creates corresponding property parameter there.
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1115,6 +1115,11 @@
|
|||||||
<category>Kotlin</category>
|
<category>Kotlin</category>
|
||||||
</intentionAction>
|
</intentionAction>
|
||||||
|
|
||||||
|
<intentionAction>
|
||||||
|
<className>org.jetbrains.kotlin.idea.intentions.ImplementAbstractMemberAsConstructorParameterIntention</className>
|
||||||
|
<category>Kotlin</category>
|
||||||
|
</intentionAction>
|
||||||
|
|
||||||
<intentionAction>
|
<intentionAction>
|
||||||
<className>org.jetbrains.kotlin.idea.intentions.AddValVarToConstructorParameterAction$Intention</className>
|
<className>org.jetbrains.kotlin.idea.intentions.AddValVarToConstructorParameterAction$Intention</className>
|
||||||
<category>Kotlin</category>
|
<category>Kotlin</category>
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import com.intellij.ide.util.PsiElementListCellRenderer
|
|||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.diagnostic.Logger
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.openapi.ui.popup.PopupChooserBuilder
|
import com.intellij.openapi.ui.popup.PopupChooserBuilder
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
import com.intellij.psi.PsiClass
|
import com.intellij.psi.PsiClass
|
||||||
@@ -40,7 +39,6 @@ import org.jetbrains.kotlin.idea.caches.resolve.getJavaClassDescriptor
|
|||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||||
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideImplementMembersHandler
|
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideImplementMembersHandler
|
||||||
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject
|
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject
|
||||||
import org.jetbrains.kotlin.idea.refactoring.canRefactor
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass
|
import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass
|
||||||
import org.jetbrains.kotlin.idea.refactoring.runSynchronouslyWithProgress
|
import org.jetbrains.kotlin.idea.refactoring.runSynchronouslyWithProgress
|
||||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
|
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
|
||||||
@@ -48,6 +46,7 @@ import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors
|
|||||||
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
||||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||||
@@ -57,10 +56,10 @@ import org.jetbrains.kotlin.utils.addToStdlib.singletonList
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.swing.ListSelectionModel
|
import javax.swing.ListSelectionModel
|
||||||
|
|
||||||
class ImplementAbstractMemberIntention :
|
abstract class ImplementAbstractMemberIntentionBase :
|
||||||
SelfTargetingRangeIntention<KtNamedDeclaration>(KtNamedDeclaration::class.java, "", "Implement abstract member") {
|
SelfTargetingRangeIntention<KtNamedDeclaration>(KtNamedDeclaration::class.java, "", "Implement abstract member") {
|
||||||
companion object {
|
companion object {
|
||||||
private val LOG = Logger.getInstance("#${ImplementAbstractMemberIntention::class.java.canonicalName}")
|
private val LOG = Logger.getInstance("#${ImplementAbstractMemberIntentionBase::class.java.canonicalName}")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isAbstract(element: KtNamedDeclaration): Boolean {
|
private fun isAbstract(element: KtNamedDeclaration): Boolean {
|
||||||
@@ -73,7 +72,7 @@ class ImplementAbstractMemberIntention :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findExistingImplementation(
|
protected fun findExistingImplementation(
|
||||||
subClass: ClassDescriptor,
|
subClass: ClassDescriptor,
|
||||||
superMember: CallableMemberDescriptor
|
superMember: CallableMemberDescriptor
|
||||||
): CallableMemberDescriptor? {
|
): CallableMemberDescriptor? {
|
||||||
@@ -83,6 +82,8 @@ class ImplementAbstractMemberIntention :
|
|||||||
if (subMember?.kind?.isReal ?: false) return subMember else return null
|
if (subMember?.kind?.isReal ?: false) return subMember else return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract fun acceptSubClass(subClassDescriptor: ClassDescriptor, memberDescriptor: CallableMemberDescriptor): Boolean
|
||||||
|
|
||||||
private fun findClassesToProcess(member: KtNamedDeclaration): Sequence<PsiElement> {
|
private fun findClassesToProcess(member: KtNamedDeclaration): Sequence<PsiElement> {
|
||||||
val baseClass = member.containingClassOrObject as? KtClass ?: return emptySequence()
|
val baseClass = member.containingClassOrObject as? KtClass ?: return emptySequence()
|
||||||
val memberDescriptor = member.resolveToDescriptorIfAny() as? CallableMemberDescriptor ?: return emptySequence()
|
val memberDescriptor = member.resolveToDescriptorIfAny() as? CallableMemberDescriptor ?: return emptySequence()
|
||||||
@@ -94,7 +95,7 @@ class ImplementAbstractMemberIntention :
|
|||||||
is PsiClass -> subClass.getJavaClassDescriptor()
|
is PsiClass -> subClass.getJavaClassDescriptor()
|
||||||
else -> null
|
else -> null
|
||||||
} as? ClassDescriptor ?: return false
|
} as? ClassDescriptor ?: return false
|
||||||
return classDescriptor.kind != ClassKind.INTERFACE && findExistingImplementation(classDescriptor, memberDescriptor) == null
|
return acceptSubClass(classDescriptor, memberDescriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (baseClass.isEnum()) {
|
if (baseClass.isEnum()) {
|
||||||
@@ -110,20 +111,20 @@ class ImplementAbstractMemberIntention :
|
|||||||
.filter(::acceptSubClass)
|
.filter(::acceptSubClass)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract fun computeText(element: KtNamedDeclaration): String?
|
||||||
|
|
||||||
override fun applicabilityRange(element: KtNamedDeclaration): TextRange? {
|
override fun applicabilityRange(element: KtNamedDeclaration): TextRange? {
|
||||||
if (!isAbstract(element)) return null
|
if (!isAbstract(element)) return null
|
||||||
|
|
||||||
text = when(element) {
|
text = computeText(element) ?: return null
|
||||||
is KtProperty -> "Implement abstract property"
|
|
||||||
is KtNamedFunction -> "Implement abstract function"
|
|
||||||
else -> return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!findClassesToProcess(element).any()) return null
|
if (!findClassesToProcess(element).any()) return null
|
||||||
|
|
||||||
return element.nameIdentifier?.textRange
|
return element.nameIdentifier?.textRange
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract val preferConstructorParameters: Boolean
|
||||||
|
|
||||||
private fun implementInKotlinClass(member: KtNamedDeclaration, targetClass: KtClassOrObject) {
|
private fun implementInKotlinClass(member: KtNamedDeclaration, targetClass: KtClassOrObject) {
|
||||||
val subClassDescriptor = targetClass.resolveToDescriptorIfAny() as? ClassDescriptor ?: return
|
val subClassDescriptor = targetClass.resolveToDescriptorIfAny() as? ClassDescriptor ?: return
|
||||||
val superMemberDescriptor = member.resolveToDescriptorIfAny() as? CallableMemberDescriptor ?: return
|
val superMemberDescriptor = member.resolveToDescriptorIfAny() as? CallableMemberDescriptor ?: return
|
||||||
@@ -134,7 +135,8 @@ class ImplementAbstractMemberIntention :
|
|||||||
val chooserObject = OverrideMemberChooserObject.create(member.project,
|
val chooserObject = OverrideMemberChooserObject.create(member.project,
|
||||||
descriptorToImplement,
|
descriptorToImplement,
|
||||||
descriptorToImplement,
|
descriptorToImplement,
|
||||||
OverrideMemberChooserObject.BodyType.EMPTY)
|
OverrideMemberChooserObject.BodyType.EMPTY,
|
||||||
|
preferConstructorParameters)
|
||||||
OverrideImplementMembersHandler.generateMembers(null, targetClass, chooserObject.singletonList())
|
OverrideImplementMembersHandler.generateMembers(null, targetClass, chooserObject.singletonList())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,4 +231,43 @@ class ImplementAbstractMemberIntention :
|
|||||||
.createPopup()
|
.createPopup()
|
||||||
.showInBestPositionFor(editor)
|
.showInBestPositionFor(editor)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImplementAbstractMemberIntention : ImplementAbstractMemberIntentionBase() {
|
||||||
|
override fun computeText(element: KtNamedDeclaration): String? {
|
||||||
|
return when(element) {
|
||||||
|
is KtProperty -> "Implement abstract property"
|
||||||
|
is KtNamedFunction -> "Implement abstract function"
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun acceptSubClass(subClassDescriptor: ClassDescriptor, memberDescriptor: CallableMemberDescriptor): Boolean {
|
||||||
|
return subClassDescriptor.kind != ClassKind.INTERFACE && findExistingImplementation(subClassDescriptor, memberDescriptor) == null
|
||||||
|
}
|
||||||
|
|
||||||
|
override val preferConstructorParameters: Boolean
|
||||||
|
get() = false
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImplementAbstractMemberAsConstructorParameterIntention : ImplementAbstractMemberIntentionBase() {
|
||||||
|
override fun computeText(element: KtNamedDeclaration): String? {
|
||||||
|
if (element !is KtProperty) return null
|
||||||
|
return "Implement as constructor parameter"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun acceptSubClass(subClassDescriptor: ClassDescriptor, memberDescriptor: CallableMemberDescriptor): Boolean {
|
||||||
|
val kind = subClassDescriptor.kind
|
||||||
|
return (kind == ClassKind.CLASS || kind == ClassKind.ENUM_CLASS)
|
||||||
|
&& subClassDescriptor !is JavaClassDescriptor
|
||||||
|
&& findExistingImplementation(subClassDescriptor, memberDescriptor) == null
|
||||||
|
}
|
||||||
|
|
||||||
|
override val preferConstructorParameters: Boolean
|
||||||
|
get() = true
|
||||||
|
|
||||||
|
override fun applicabilityRange(element: KtNamedDeclaration): TextRange? {
|
||||||
|
if (element !is KtProperty) return null
|
||||||
|
return super.applicabilityRange(element)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,6 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
|||||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||||
import org.jetbrains.kotlin.idea.core.isVisible
|
import org.jetbrains.kotlin.idea.core.isVisible
|
||||||
import org.jetbrains.kotlin.idea.refactoring.createPrimaryConstructorParameterListIfAbsent
|
|
||||||
import org.jetbrains.kotlin.idea.core.replaced
|
import org.jetbrains.kotlin.idea.core.replaced
|
||||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||||
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
||||||
|
|||||||
+2
-3
@@ -26,15 +26,14 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
|||||||
import org.jetbrains.kotlin.idea.caches.resolve.getJavaMethodDescriptor
|
import org.jetbrains.kotlin.idea.caches.resolve.getJavaMethodDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||||
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
|
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
|
||||||
import org.jetbrains.kotlin.idea.refactoring.createPrimaryConstructorIfAbsent
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.dropOverrideKeywordIfNecessary
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.replaceListPsiAndKeepDelimiters
|
|
||||||
import org.jetbrains.kotlin.idea.core.setVisibility
|
import org.jetbrains.kotlin.idea.core.setVisibility
|
||||||
import org.jetbrains.kotlin.idea.core.toKeywordToken
|
import org.jetbrains.kotlin.idea.core.toKeywordToken
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinChangeInfo
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinChangeInfo
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinParameterInfo
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinParameterInfo
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinValVar
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinValVar
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.getCallableSubstitutor
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.getCallableSubstitutor
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.dropOverrideKeywordIfNecessary
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.replaceListPsiAndKeepDelimiters
|
||||||
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
||||||
import org.jetbrains.kotlin.idea.util.ShortenReferences.Options
|
import org.jetbrains.kotlin.idea.util.ShortenReferences.Options
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
|||||||
-1
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.idea.refactoring.changeSignature.usages
|
|||||||
|
|
||||||
import com.intellij.usageView.UsageInfo
|
import com.intellij.usageView.UsageInfo
|
||||||
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
|
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
|
||||||
import org.jetbrains.kotlin.idea.refactoring.createPrimaryConstructorParameterListIfAbsent
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinChangeInfo
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinChangeInfo
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.getAffectedCallables
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.getAffectedCallables
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.isInsideOfCallerBody
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.isInsideOfCallerBody
|
||||||
|
|||||||
-1
@@ -30,7 +30,6 @@ import com.intellij.openapi.util.TextRange
|
|||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.ui.JBColor
|
import com.intellij.ui.JBColor
|
||||||
import com.intellij.ui.NonFocusableCheckBox
|
import com.intellij.ui.NonFocusableCheckBox
|
||||||
import org.jetbrains.kotlin.idea.refactoring.createPrimaryConstructorParameterListIfAbsent
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinValVar
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinValVar
|
||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.AbstractKotlinInplaceIntroducer
|
import org.jetbrains.kotlin.idea.refactoring.introduce.AbstractKotlinInplaceIntroducer
|
||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinInplaceVariableIntroducer
|
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinInplaceVariableIntroducer
|
||||||
|
|||||||
@@ -701,22 +701,6 @@ public fun String.quoteIfNeeded(): String = if (KotlinNameSuggester.isIdentifier
|
|||||||
|
|
||||||
public fun FqNameUnsafe.hasIdentifiersOnly(): Boolean = pathSegments().all { KotlinNameSuggester.isIdentifier(it.asString()) }
|
public fun FqNameUnsafe.hasIdentifiersOnly(): Boolean = pathSegments().all { KotlinNameSuggester.isIdentifier(it.asString()) }
|
||||||
|
|
||||||
public fun KtClass.createPrimaryConstructorIfAbsent(): KtPrimaryConstructor {
|
|
||||||
val constructor = getPrimaryConstructor()
|
|
||||||
if (constructor != null) return constructor
|
|
||||||
var anchor: PsiElement? = typeParameterList
|
|
||||||
if (anchor == null) anchor = nameIdentifier
|
|
||||||
if (anchor == null) anchor = lastChild
|
|
||||||
return addAfter(KtPsiFactory(project).createPrimaryConstructor(), anchor) as KtPrimaryConstructor
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun KtClass.createPrimaryConstructorParameterListIfAbsent(): KtParameterList {
|
|
||||||
val constructor = createPrimaryConstructorIfAbsent()
|
|
||||||
val parameterList = constructor.valueParameterList
|
|
||||||
if (parameterList != null) return parameterList
|
|
||||||
return constructor.add(KtPsiFactory(project).createParameterList("()")) as KtParameterList
|
|
||||||
}
|
|
||||||
|
|
||||||
fun PsiNamedElement.isInterfaceClass(): Boolean = this is KtClass && isInterface() || this is PsiClass && isInterface
|
fun PsiNamedElement.isInterfaceClass(): Boolean = this is KtClass && isInterface() || this is PsiClass && isInterface
|
||||||
|
|
||||||
fun <ListType : KtElement> replaceListPsiAndKeepDelimiters(
|
fun <ListType : KtElement> replaceListPsiAndKeepDelimiters(
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
|
|||||||
import org.jetbrains.kotlin.idea.core.dropDefaultValue
|
import org.jetbrains.kotlin.idea.core.dropDefaultValue
|
||||||
import org.jetbrains.kotlin.idea.intentions.setType
|
import org.jetbrains.kotlin.idea.intentions.setType
|
||||||
import org.jetbrains.kotlin.idea.refactoring.createJavaField
|
import org.jetbrains.kotlin.idea.refactoring.createJavaField
|
||||||
import org.jetbrains.kotlin.idea.refactoring.createPrimaryConstructorIfAbsent
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.safeDelete.removeOverrideModifier
|
import org.jetbrains.kotlin.idea.refactoring.safeDelete.removeOverrideModifier
|
||||||
import org.jetbrains.kotlin.idea.util.anonymousObjectSuperTypeOrNull
|
import org.jetbrains.kotlin.idea.util.anonymousObjectSuperTypeOrNull
|
||||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.KotlinPsiUnifier
|
import org.jetbrains.kotlin.idea.util.psi.patternMatching.KotlinPsiUnifier
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
org.jetbrains.kotlin.idea.intentions.ImplementAbstractMemberAsConstructorParameterIntention
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// DISABLE-ERRORS
|
||||||
|
interface T<X> {
|
||||||
|
val <caret>foo: X
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class E : T<Int> {
|
||||||
|
A, B, C
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// DISABLE-ERRORS
|
||||||
|
interface T<X> {
|
||||||
|
val <caret>foo: X
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class E(override val foo: Int) : T<Int> {
|
||||||
|
A, B, C
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
// DISABLE-ERRORS
|
||||||
|
interface A {
|
||||||
|
abstract fun <caret>foo(): Int
|
||||||
|
}
|
||||||
|
|
||||||
|
class B : A {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// DISABLE-ERRORS
|
||||||
|
interface T<X> {
|
||||||
|
val <caret>foo: X
|
||||||
|
}
|
||||||
|
|
||||||
|
class U : T<String> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class V : T<Int> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Z : T<Int> by V() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class W : T<Boolean> {
|
||||||
|
override val foo: Boolean
|
||||||
|
get() = throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// DISABLE-ERRORS
|
||||||
|
interface T<X> {
|
||||||
|
val <caret>foo: X
|
||||||
|
}
|
||||||
|
|
||||||
|
class U(override val foo: String) : T<String> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class V(override val foo: Int) : T<Int> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Z : T<Int> by V() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class W : T<Boolean> {
|
||||||
|
override val foo: Boolean
|
||||||
|
get() = throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
// DISABLE-ERRORS
|
||||||
|
enum class E {
|
||||||
|
A, B, C;
|
||||||
|
|
||||||
|
abstract val <caret>foo: Int
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
// ERROR: Abstract property 'foo' in non-abstract class 'A'
|
||||||
|
class A {
|
||||||
|
abstract val <caret>foo: Int
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
// ERROR: Abstract property 'foo' in non-abstract class 'A'
|
||||||
|
object A {
|
||||||
|
abstract val <caret>foo: Int
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
// ERROR: Class 'C' must be declared abstract or implement abstract base class member public abstract val foo: kotlin.Int defined in B
|
||||||
|
interface A {
|
||||||
|
val <caret>foo: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
class X : A {
|
||||||
|
override val foo = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class B : A {
|
||||||
|
abstract override val foo: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
class C: B() {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
interface A {
|
||||||
|
val <caret>foo: Int
|
||||||
|
}
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
open class A {
|
||||||
|
val <caret>foo = 1
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
// ERROR: Property must be initialized or be abstract
|
||||||
|
open class A {
|
||||||
|
val <caret>foo: Int
|
||||||
|
}
|
||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
interface A {
|
||||||
|
val <caret>foo: Int
|
||||||
|
get() = 1
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package source;
|
||||||
|
|
||||||
|
abstract class X<S> implements T<S> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Y implements T<String> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Z implements T<Boolean> {
|
||||||
|
@Override
|
||||||
|
public Boolean getFoo() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface U extends T<Object> {
|
||||||
|
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package source
|
||||||
|
|
||||||
|
interface T<X> {
|
||||||
|
val foo: X
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package source;
|
||||||
|
|
||||||
|
abstract class X<S> implements T<S> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Y implements T<String> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Z implements T<Boolean> {
|
||||||
|
@Override
|
||||||
|
public Boolean getFoo() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface U extends T<Object> {
|
||||||
|
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package source
|
||||||
|
|
||||||
|
interface T<X> {
|
||||||
|
val <caret>foo: X
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"mainFile": "source/test.kt",
|
||||||
|
"intentionClass": "org.jetbrains.kotlin.idea.intentions.ImplementAbstractMemberAsConstructorParameterIntention",
|
||||||
|
"isApplicable": "false",
|
||||||
|
"withRuntime": "true"
|
||||||
|
}
|
||||||
@@ -5464,6 +5464,81 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/testData/intentions/implementAsConstructorParameter")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class ImplementAsConstructorParameter extends AbstractIntentionTest {
|
||||||
|
public void testAllFilesPresentInImplementAsConstructorParameter() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/implementAsConstructorParameter"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("enumClass.kt")
|
||||||
|
public void testEnumClass() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAsConstructorParameter/enumClass.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("function.kt")
|
||||||
|
public void testFunction() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAsConstructorParameter/function.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("implementAll.kt")
|
||||||
|
public void testImplementAll() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAsConstructorParameter/implementAll.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inEnumClass.kt")
|
||||||
|
public void testInEnumClass() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAsConstructorParameter/inEnumClass.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inFinalClass.kt")
|
||||||
|
public void testInFinalClass() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAsConstructorParameter/inFinalClass.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inObject.kt")
|
||||||
|
public void testInObject() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAsConstructorParameter/inObject.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("noDirectOverridesNeeded.kt")
|
||||||
|
public void testNoDirectOverridesNeeded() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAsConstructorParameter/noDirectOverridesNeeded.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("noInheritors.kt")
|
||||||
|
public void testNoInheritors() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAsConstructorParameter/noInheritors.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("notAbstractInClass.kt")
|
||||||
|
public void testNotAbstractInClass() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAsConstructorParameter/notAbstractInClass.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("notAbstractNoBodyInClass.kt")
|
||||||
|
public void testNotAbstractNoBodyInClass() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAsConstructorParameter/notAbstractNoBodyInClass.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("notAbstractWithGetterInInterface.kt")
|
||||||
|
public void testNotAbstractWithGetterInInterface() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAsConstructorParameter/notAbstractWithGetterInInterface.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/testData/intentions/importAllMembers")
|
@TestMetadata("idea/testData/intentions/importAllMembers")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
@@ -53,6 +53,12 @@ public class MultiFileIntentionTestGenerated extends AbstractMultiFileIntentionT
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("implementAsConstructorParameter/implementValInJava/implementAllInJava.test")
|
||||||
|
public void testImplementAsConstructorParameter_implementValInJava_ImplementAllInJava() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/multiFileIntentions/implementAsConstructorParameter/implementValInJava/implementAllInJava.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("moveDeclarationToSeparateFile/moveClassToExistingFile/moveClassToExistingFile.test")
|
@TestMetadata("moveDeclarationToSeparateFile/moveClassToExistingFile/moveClassToExistingFile.test")
|
||||||
public void testMoveDeclarationToSeparateFile_moveClassToExistingFile_MoveClassToExistingFile() throws Exception {
|
public void testMoveDeclarationToSeparateFile_moveClassToExistingFile_MoveClassToExistingFile() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/multiFileIntentions/moveDeclarationToSeparateFile/moveClassToExistingFile/moveClassToExistingFile.test");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/multiFileIntentions/moveDeclarationToSeparateFile/moveClassToExistingFile/moveClassToExistingFile.test");
|
||||||
|
|||||||
Reference in New Issue
Block a user