Create from Usage: Support primary constructor insertion

This commit is contained in:
Alexey Sedunov
2017-07-17 16:06:09 +03:00
parent 877874978f
commit d44313876c
9 changed files with 36 additions and 27 deletions
@@ -432,12 +432,12 @@ class KtPsiFactory @JvmOverloads constructor(private val project: Project, val m
return file.importDirectives
}
fun createPrimaryConstructor(): KtPrimaryConstructor {
return createClass("class A()").primaryConstructor!!
fun createPrimaryConstructor(text: String = ""): KtPrimaryConstructor {
return createClass(if (text.isNotEmpty())"class A $text" else "class A()" ).primaryConstructor!!
}
fun createPrimaryConstructor(modifiers: String?): KtPrimaryConstructor {
return modifiers?.let { createClass("class A $modifiers constructor()").primaryConstructor } ?: createPrimaryConstructor()
fun createPrimaryConstructorWithModifiers(modifiers: String?): KtPrimaryConstructor {
return modifiers?.let { createPrimaryConstructor("$it constructor()") } ?: createPrimaryConstructor()
}
fun createConstructorKeyword(): PsiElement =
@@ -158,7 +158,7 @@ class ConvertSecondaryConstructorToPrimaryIntention : SelfTargetingRangeIntentio
val factory = KtPsiFactory(klass)
val constructorCommentSaver = CommentSaver(element)
val constructorInClass = klass.createPrimaryConstructorIfAbsent()
val constructor = factory.createPrimaryConstructor(element.modifierList?.text?.replace("\n", " "))
val constructor = factory.createPrimaryConstructorWithModifiers(element.modifierList?.text?.replace("\n", " "))
val parameterToPropertyMap = mutableMapOf<ValueParameterDescriptor, PropertyDescriptor>()
val initializer = element.extractInitializer(parameterToPropertyMap, context, factory) ?: return
@@ -322,7 +322,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
returnTypeCandidate?.theType?.isUnit() ?: false
CallableKind.CLASS_WITH_PRIMARY_CONSTRUCTOR ->
callableInfo.returnTypeInfo == TypeInfo.Empty || returnTypeCandidate?.theType?.isAnyOrNullableAny() ?: false
CallableKind.SECONDARY_CONSTRUCTOR -> true
CallableKind.CONSTRUCTOR -> true
CallableKind.PROPERTY -> containingElement is KtBlockExpression
}
@@ -432,18 +432,18 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
if (isFunctionType) "($renderedType)." else "$renderedType."
} else ""
val classKind = (callableInfo as? PrimaryConstructorInfo)?.classInfo?.kind
val classKind = (callableInfo as? ClassWithPrimaryConstructorInfo)?.classInfo?.kind
fun renderParamList(): String {
val prefix = if (classKind == ClassKind.ANNOTATION_CLASS) "val " else ""
val list = callableInfo.parameterInfos.indices.joinToString(", ") { i -> "${prefix}p$i: Any" }
return if (callableInfo.parameterInfos.isNotEmpty()
|| callableInfo.kind == CallableKind.FUNCTION
|| callableInfo.kind == CallableKind.SECONDARY_CONSTRUCTOR) "($list)" else list
|| callableInfo.kind == CallableKind.CONSTRUCTOR) "($list)" else list
}
val paramList = when (callableInfo.kind) {
CallableKind.FUNCTION, CallableKind.CLASS_WITH_PRIMARY_CONSTRUCTOR, CallableKind.SECONDARY_CONSTRUCTOR ->
CallableKind.FUNCTION, CallableKind.CLASS_WITH_PRIMARY_CONSTRUCTOR, CallableKind.CONSTRUCTOR ->
renderParamList()
CallableKind.PROPERTY -> ""
}
@@ -459,7 +459,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
else if (containingElement is KtClassOrObject
&& !(containingElement is KtClass && containingElement.isInterface())
&& containingElement.isAncestor(config.originalElement)
&& callableInfo.kind != CallableKind.SECONDARY_CONSTRUCTOR) "private "
&& callableInfo.kind != CallableKind.CONSTRUCTOR) "private "
else if (isExtension) "private "
else ""
@@ -468,9 +468,9 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
}
val declaration: KtNamedDeclaration = when (callableInfo.kind) {
CallableKind.FUNCTION, CallableKind.SECONDARY_CONSTRUCTOR -> {
CallableKind.FUNCTION, CallableKind.CONSTRUCTOR -> {
val body = when {
callableInfo.kind == CallableKind.SECONDARY_CONSTRUCTOR -> ""
callableInfo.kind == CallableKind.CONSTRUCTOR -> ""
callableInfo.isAbstract -> ""
containingElement is KtClass && containingElement.hasModifier(KtTokens.EXTERNAL_KEYWORD) -> ""
containingElement is KtObjectDeclaration && containingElement.hasModifier(KtTokens.EXTERNAL_KEYWORD) -> ""
@@ -487,12 +487,16 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
val infixModifier = if (callableInfo.isInfix) "infix " else ""
psiFactory.createFunction("$modifiers$infixModifier${operatorModifier}fun<> $header $body") as KtNamedDeclaration
}
else if ((callableInfo as ConstructorInfo).isPrimary) {
val constructorText = if (modifiers.isNotEmpty()) "${modifiers}constructor$paramList" else paramList
psiFactory.createPrimaryConstructor(constructorText) as KtNamedDeclaration
}
else {
psiFactory.createSecondaryConstructor("${modifiers}constructor$paramList $body") as KtNamedDeclaration
}
}
CallableKind.CLASS_WITH_PRIMARY_CONSTRUCTOR -> {
with((callableInfo as PrimaryConstructorInfo).classInfo) {
with((callableInfo as ClassWithPrimaryConstructorInfo).classInfo) {
val classBody = when (kind) {
ClassKind.ANNOTATION_CLASS, ClassKind.ENUM_ENTRY -> ""
else -> "{\n\n}"
@@ -824,7 +828,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
}
val needStatic = when (callableInfo) {
is PrimaryConstructorInfo -> with(callableInfo.classInfo) {
is ClassWithPrimaryConstructorInfo -> with(callableInfo.classInfo) {
!inner && kind != ClassKind.ENUM_ENTRY && kind != ClassKind.ENUM_CLASS
}
else -> callableInfo.receiverTypeInfo.staticContextRequired
@@ -1052,6 +1056,10 @@ internal fun <D : KtNamedDeclaration> placeDeclarationInContainer(
}
val declarationInPlace = when {
declaration is KtPrimaryConstructor -> {
(container as KtClass).createPrimaryConstructorIfAbsent().replaced(declaration) as D
}
actualContainer.isAncestor(anchor, true) -> {
val insertToBlock = container is KtBlockExpression
if (insertToBlock) {
@@ -161,7 +161,7 @@ class ParameterInfo(
enum class CallableKind {
FUNCTION,
CLASS_WITH_PRIMARY_CONSTRUCTOR,
SECONDARY_CONSTRUCTOR,
CONSTRUCTOR,
PROPERTY
}
@@ -206,7 +206,7 @@ class FunctionInfo(name: String,
)
}
class PrimaryConstructorInfo(val classInfo: ClassInfo, expectedTypeInfo: TypeInfo): CallableInfo(
class ClassWithPrimaryConstructorInfo(val classInfo: ClassInfo, expectedTypeInfo: TypeInfo): CallableInfo(
classInfo.name, TypeInfo.Empty, expectedTypeInfo.forceNotNull(), Collections.emptyList(), classInfo.typeArguments, false
) {
override val kind: CallableKind get() = CallableKind.CLASS_WITH_PRIMARY_CONSTRUCTOR
@@ -215,11 +215,12 @@ class PrimaryConstructorInfo(val classInfo: ClassInfo, expectedTypeInfo: TypeInf
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) = throw UnsupportedOperationException()
}
class SecondaryConstructorInfo(
class ConstructorInfo(
override val parameterInfos: List<ParameterInfo>,
val targetClass: PsiElement
val targetClass: PsiElement,
val isPrimary: Boolean = false
): CallableInfo("", TypeInfo.Empty, TypeInfo.Empty, Collections.emptyList(), Collections.emptyList(), false) {
override val kind: CallableKind get() = CallableKind.SECONDARY_CONSTRUCTOR
override val kind: CallableKind get() = CallableKind.CONSTRUCTOR
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) = throw UnsupportedOperationException()
}
@@ -322,7 +322,7 @@ sealed class CreateCallableFromCallActionFactory<E : KtExpression>(
val parameters = expression.getParameterInfos()
return SecondaryConstructorInfo(parameters, klass)
return ConstructorInfo(parameters, klass)
}
}
@@ -89,7 +89,7 @@ abstract class CreateCallableFromUsageFixBase<E : KtElement>(
val kind = when (it.kind) {
CallableKind.FUNCTION -> "function"
CallableKind.PROPERTY -> "property"
CallableKind.SECONDARY_CONSTRUCTOR -> "secondary constructor"
CallableKind.CONSTRUCTOR -> "secondary constructor"
else -> throw AssertionError("Unexpected callable info: $it")
}
append(kind)
@@ -185,7 +185,7 @@ abstract class CreateCallableFromUsageFixBase<E : KtElement>(
project.executeCommand(text) { callableBuilder.build() }
}
if (callableInfo is SecondaryConstructorInfo) {
if (callableInfo is ConstructorInfo) {
runBuilder(CallablePlacement.NoReceiver(callableInfo.targetClass))
return
}
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ParameterInfo
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.SecondaryConstructorInfo
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ConstructorInfo
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
import org.jetbrains.kotlin.idea.refactoring.canRefactor
import org.jetbrains.kotlin.psi.KtClass
@@ -65,6 +65,6 @@ object CreateConstructorFromDelegationCallActionFactory : CreateCallableMemberFr
)
}
return SecondaryConstructorInfo(parameters, targetClass)
return ConstructorInfo(parameters, targetClass)
}
}
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.refactoring.canRefactor
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ParameterInfo
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.SecondaryConstructorInfo
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ConstructorInfo
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtSuperTypeCallEntry
@@ -58,6 +58,6 @@ object CreateConstructorFromSuperTypeCallActionFactory : CreateCallableMemberFro
)
}
return SecondaryConstructorInfo(parameters, targetClass)
return ConstructorInfo(parameters, targetClass)
}
}
@@ -163,7 +163,7 @@ open class CreateClassFromUsageFix<E : KtElement> protected constructor (
is PsiPackage -> createFileByPackage(selectedParent, editor, file)
else -> throw AssertionError("Unexpected element: " + selectedParent.text)
} ?: return@runWriteAction
val constructorInfo = PrimaryConstructorInfo(classInfo, expectedTypeInfo)
val constructorInfo = ClassWithPrimaryConstructorInfo(classInfo, expectedTypeInfo)
val builder = CallableBuilderConfiguration(
Collections.singletonList(constructorInfo),
element as KtElement,