Create from Usage: Secondary constructor support

This commit is contained in:
Alexey Sedunov
2015-03-26 14:14:37 +03:00
parent 11675322ee
commit 15dddf362e
8 changed files with 174 additions and 110 deletions
@@ -207,6 +207,10 @@ public class JetPsiFactory(private val project: Project) {
return createDeclaration(funDecl) return createDeclaration(funDecl)
} }
public fun createSecondaryConstructor(decl: String): JetSecondaryConstructor {
return createClass("class Foo {\n $decl \n}").getSecondaryConstructors().first()
}
public fun createModifierList(modifier: JetKeywordToken): JetModifierList { public fun createModifierList(modifier: JetKeywordToken): JetModifierList {
return createModifierList(modifier.getValue()) return createModifierList(modifier.getValue())
} }
@@ -0,0 +1 @@
//To change body of created constructors use File | Settings | File Templates.
@@ -0,0 +1,28 @@
<html>
<body>
<table border="0" cellpadding="2" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111">
<tr>
<td colspan="3"><font face="verdana" size="-1">This is a built-in template used for filling the body of a Kotlin secondary
constructor each time it is generated by the program, e.g. when using the <b>Create secondary constructor from Usage</b> intention action.<br>
The template is editable. Along with Kotlin expressions and comments, you can also use the predefined variables
that will be then expanded into the corresponding values.</font>
</td>
</tr>
</table>
<table border="0" cellpadding="5" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111">
<tr>
<td colspan="3"><font face="verdana" size="-1">Predefined variables will take the following values:</font></td>
</tr>
<tr>
<td valign="top"><nobr><font face="verdana" size="-2" color="#7F0000"><b><i>${CLASS_NAME}</i></b></font></nobr></td>
<td width="10">&nbsp;</td>
<td valign="top"><font face="verdana" size="-1">qualified name of the class where function is created</font></td>
</tr>
<tr>
<td valign="top"><nobr><font face="verdana" size="-2" color="#7F0000"><b><i>${SIMPLE_CLASS_NAME}</i></b></font></nobr></td>
<td width="10">&nbsp;</td>
<td valign="top"><font face="verdana" size="-1">non-qualified name of the class where function is implemented</font></td>
</tr>
</table>
</body>
</html>
@@ -16,73 +16,61 @@
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder package org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder
import com.intellij.codeInsight.daemon.impl.quickfix.CreateFromUsageUtils
import com.intellij.codeInsight.navigation.NavigationUtil import com.intellij.codeInsight.navigation.NavigationUtil
import com.intellij.codeInsight.template.* import com.intellij.codeInsight.template.*
import com.intellij.codeInsight.template.impl.TemplateImpl
import com.intellij.codeInsight.template.impl.TemplateManagerImpl
import com.intellij.ide.fileTemplates.FileTemplate import com.intellij.ide.fileTemplates.FileTemplate
import com.intellij.ide.fileTemplates.FileTemplateManager import com.intellij.ide.fileTemplates.FileTemplateManager
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.ScrollType
import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.fileEditor.OpenFileDescriptor
import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.psi.PsiDocumentManager import com.intellij.openapi.ui.DialogWrapper
import com.intellij.psi.*
import com.intellij.psi.codeStyle.JavaCodeStyleManager
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.IncorrectOperationException import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import kotlin.properties.Delegates import org.jetbrains.kotlin.descriptors.*
import java.util.LinkedHashSet
import java.util.Collections
import java.util.HashMap
import java.util.ArrayList
import java.util.Properties
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.types.checker.JetTypeChecker
import com.intellij.psi.SmartPointerManager
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl
import java.util.LinkedHashMap import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.ClassKind
import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList
import com.intellij.openapi.editor.ScrollType
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import com.intellij.psi.PsiClass
import com.intellij.codeInsight.daemon.impl.quickfix.CreateFromUsageUtils
import com.intellij.psi.PsiMethod
import com.intellij.openapi.fileEditor.OpenFileDescriptor
import com.intellij.psi.codeStyle.JavaCodeStyleManager
import com.intellij.psi.PsiModifier
import com.intellij.psi.PsiField
import com.intellij.psi.PsiMember
import org.jetbrains.kotlin.idea.caches.resolve.getJavaClassDescriptor import org.jetbrains.kotlin.idea.caches.resolve.getJavaClassDescriptor
import org.jetbrains.kotlin.resolve.scopes.ChainedScope import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
import org.jetbrains.kotlin.resolve.scopes.WritableScopeImpl import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.ClassKind
import org.jetbrains.kotlin.resolve.scopes.RedeclarationHandler
import org.jetbrains.kotlin.resolve.scopes.WritableScope
import com.intellij.psi.PsiMethodCallExpression
import com.intellij.psi.PsiExpressionStatement
import org.jetbrains.kotlin.idea.refactoring.* import org.jetbrains.kotlin.idea.refactoring.*
import org.jetbrains.kotlin.idea.util.* import org.jetbrains.kotlin.idea.util.*
import com.intellij.openapi.ui.* import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import com.intellij.codeInsight.template.impl.* import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.idea.util.application.* import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.psi.psiUtil.siblings
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.scopes.*
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeProjectionImpl
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.checker.JetTypeChecker
import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList
import java.util.*
import kotlin.properties.Delegates
private val TYPE_PARAMETER_LIST_VARIABLE_NAME = "typeParameterList" private val TYPE_PARAMETER_LIST_VARIABLE_NAME = "typeParameterList"
private val TEMPLATE_FROM_USAGE_FUNCTION_BODY = "New Kotlin Function Body.kt" private val TEMPLATE_FROM_USAGE_FUNCTION_BODY = "New Kotlin Function Body.kt"
private val TEMPLATE_FROM_USAGE_SECONDARY_CONSTRUCTOR_BODY = "New Kotlin Secondary Constructor Body.kt"
private val ATTRIBUTE_FUNCTION_NAME = "FUNCTION_NAME" private val ATTRIBUTE_FUNCTION_NAME = "FUNCTION_NAME"
/** /**
@@ -322,8 +310,9 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
skipReturnType = when (callableInfo.kind) { skipReturnType = when (callableInfo.kind) {
CallableKind.FUNCTION -> CallableKind.FUNCTION ->
returnTypeCandidate?.theType?.isUnit() ?: false returnTypeCandidate?.theType?.isUnit() ?: false
CallableKind.CONSTRUCTOR -> CallableKind.CLASS_WITH_PRIMARY_CONSTRUCTOR ->
callableInfo.returnTypeInfo == TypeInfo.Empty || returnTypeCandidate?.theType?.isAny() ?: false callableInfo.returnTypeInfo == TypeInfo.Empty || returnTypeCandidate?.theType?.isAny() ?: false
CallableKind.SECONDARY_CONSTRUCTOR -> true
CallableKind.PROPERTY -> containingElement is JetBlockExpression CallableKind.PROPERTY -> containingElement is JetBlockExpression
} }
@@ -437,7 +426,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
val ownerTypeString = if (isExtension) "${receiverTypeCandidate!!.renderedType!!}." else "" val ownerTypeString = if (isExtension) "${receiverTypeCandidate!!.renderedType!!}." else ""
val classKind = (callableInfo as? ConstructorInfo)?.classInfo?.kind val classKind = (callableInfo as? PrimaryConstructorInfo)?.classInfo?.kind
fun renderParamList(): String { fun renderParamList(): String {
val prefix = if (classKind == ClassKind.ANNOTATION_CLASS) "val " else "" val prefix = if (classKind == ClassKind.ANNOTATION_CLASS) "val " else ""
@@ -446,7 +435,8 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
} }
val paramList = when (callableInfo.kind) { val paramList = when (callableInfo.kind) {
CallableKind.FUNCTION, CallableKind.CONSTRUCTOR -> renderParamList() CallableKind.FUNCTION, CallableKind.CLASS_WITH_PRIMARY_CONSTRUCTOR, CallableKind.SECONDARY_CONSTRUCTOR ->
renderParamList()
CallableKind.PROPERTY -> "" CallableKind.PROPERTY -> ""
} }
val returnTypeString = if (skipReturnType || assignmentToReplace != null) "" else ": Any" val returnTypeString = if (skipReturnType || assignmentToReplace != null) "" else ": Any"
@@ -455,20 +445,27 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
val psiFactory = JetPsiFactory(currentFile) val psiFactory = JetPsiFactory(currentFile)
val modifiers = val modifiers =
if (containingElement is JetClassOrObject && containingElement.isAncestor(config.originalElement)) if (containingElement is JetClassOrObject
&& containingElement.isAncestor(config.originalElement)
&& callableInfo.kind != CallableKind.SECONDARY_CONSTRUCTOR)
"private " "private "
else "" else ""
val declaration : JetNamedDeclaration = when (callableInfo.kind) { val declaration : JetNamedDeclaration = when (callableInfo.kind) {
CallableKind.FUNCTION -> { CallableKind.FUNCTION, CallableKind.SECONDARY_CONSTRUCTOR -> {
val body = when { val body = when {
containingElement is JetClass && containingElement.isTrait() && !config.isExtension -> "" containingElement is JetClass && containingElement.isTrait() && !config.isExtension -> ""
else -> "{}" else -> "{}"
} }
psiFactory.createFunction("${modifiers}fun<> $header $body") if (callableInfo.kind == CallableKind.FUNCTION) {
psiFactory.createFunction("${modifiers}fun<> $header $body")
}
else {
psiFactory.createSecondaryConstructor("${modifiers}constructor$paramList $body")
}
} }
CallableKind.CONSTRUCTOR -> { CallableKind.CLASS_WITH_PRIMARY_CONSTRUCTOR -> {
with((callableInfo as ConstructorInfo).classInfo) { with((callableInfo as PrimaryConstructorInfo).classInfo) {
val classBody = when (kind) { val classBody = when (kind) {
ClassKind.ANNOTATION_CLASS, ClassKind.ENUM_ENTRY -> "" ClassKind.ANNOTATION_CLASS, ClassKind.ENUM_ENTRY -> ""
else -> "{\n\n}" else -> "{\n\n}"
@@ -557,23 +554,19 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
containingElement is JetFile -> containingElement.add(declaration) as JetNamedDeclaration containingElement is JetFile -> containingElement.add(declaration) as JetNamedDeclaration
containingElement is PsiClass -> jetFileToEdit.add(declaration) as JetNamedDeclaration containingElement is PsiClass -> {
if (declaration is JetSecondaryConstructor) {
val wrappingClass = psiFactory.createClass("class ${containingElement.getName()} {\n}")
addDeclarationToClassOrObject(wrappingClass, declaration, psiFactory)
(jetFileToEdit.add(wrappingClass) as JetClass).getDeclarations().first() as JetNamedDeclaration
}
else {
jetFileToEdit.add(declaration) as JetNamedDeclaration
}
}
containingElement is JetClassOrObject -> { containingElement is JetClassOrObject -> {
var classBody = containingElement.getBody() addDeclarationToClassOrObject(containingElement, declaration, psiFactory)
if (classBody == null) {
classBody = containingElement.add(psiFactory.createEmptyClassBody()) as JetClassBody
containingElement.addBefore(psiFactory.createWhiteSpace(), classBody)
}
if (declaration is JetNamedFunction) {
val anchor = PsiTreeUtil.skipSiblingsBackward(
classBody!!.getRBrace() ?: classBody!!.getLastChild()!!,
javaClass<PsiWhiteSpace>()
)
classBody.addAfter(declaration, anchor) as JetNamedDeclaration
}
else classBody.addAfter(declaration, classBody!!.getLBrace()!!) as JetNamedDeclaration
} }
else -> throw AssertionError("Invalid containing element: ${containingElement.getText()}") else -> throw AssertionError("Invalid containing element: ${containingElement.getText()}")
} }
@@ -590,6 +583,25 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
} }
} }
private fun addDeclarationToClassOrObject(classOrObject: JetClassOrObject,
declaration: JetNamedDeclaration,
psiFactory: JetPsiFactory): JetNamedDeclaration {
var classBody = classOrObject.getBody()
if (classBody == null) {
classBody = classOrObject.add(psiFactory.createEmptyClassBody()) as JetClassBody
classOrObject.addBefore(psiFactory.createWhiteSpace(), classBody)
}
return if (declaration is JetNamedFunction) {
val anchor = PsiTreeUtil.skipSiblingsBackward(
classBody!!.getRBrace() ?: classBody!!.getLastChild()!!,
javaClass<PsiWhiteSpace>()
)
classBody.addAfter(declaration, anchor) as JetNamedDeclaration
}
else classBody.addAfter(declaration, classBody!!.getLBrace()!!) as JetNamedDeclaration
}
private fun getTypeParameterRenames(scope: JetScope): Map<TypeParameterDescriptor, String> { private fun getTypeParameterRenames(scope: JetScope): Map<TypeParameterDescriptor, String> {
val allTypeParametersNotInScope = LinkedHashSet<TypeParameterDescriptor>() val allTypeParametersNotInScope = LinkedHashSet<TypeParameterDescriptor>()
@@ -660,17 +672,24 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
return typeRefsToShorten return typeRefsToShorten
} }
private fun setupFunctionBody(func: JetNamedFunction) { private fun setupFunctionBody(func: JetFunction) {
val oldBody = func.getBodyExpression() ?: return val oldBody = func.getBodyExpression() ?: return
val fileTemplate = FileTemplateManager.getInstance()!!.getCodeTemplate(TEMPLATE_FROM_USAGE_FUNCTION_BODY) val templateName = when (func) {
is JetSecondaryConstructor -> TEMPLATE_FROM_USAGE_SECONDARY_CONSTRUCTOR_BODY
is JetNamedFunction -> TEMPLATE_FROM_USAGE_FUNCTION_BODY
else -> throw AssertionError("Unexpected declaration: " + JetPsiUtil.getElementTextWithContext(func))
}
val fileTemplate = FileTemplateManager.getInstance()!!.getCodeTemplate(templateName)
val properties = Properties() val properties = Properties()
properties.setProperty(FileTemplate.ATTRIBUTE_RETURN_TYPE, if (skipReturnType) "Unit" else func.getTypeReference()!!.getText()) properties.setProperty(FileTemplate.ATTRIBUTE_RETURN_TYPE, if (skipReturnType) "Unit" else func.getTypeReference()!!.getText())
receiverClassDescriptor?.let { receiverClassDescriptor?.let {
properties.setProperty(FileTemplate.ATTRIBUTE_CLASS_NAME, DescriptorUtils.getFqName(it).asString()) properties.setProperty(FileTemplate.ATTRIBUTE_CLASS_NAME, DescriptorUtils.getFqName(it).asString())
properties.setProperty(FileTemplate.ATTRIBUTE_SIMPLE_CLASS_NAME, it.getName().asString()) properties.setProperty(FileTemplate.ATTRIBUTE_SIMPLE_CLASS_NAME, it.getName().asString())
} }
properties.setProperty(ATTRIBUTE_FUNCTION_NAME, callableInfo.name) if (callableInfo.name.isNotEmpty()) {
properties.setProperty(ATTRIBUTE_FUNCTION_NAME, callableInfo.name)
}
val bodyText = try { val bodyText = try {
fileTemplate!!.getText(properties) fileTemplate!!.getText(properties)
@@ -766,7 +785,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
val expression = TypeParameterListExpression( val expression = TypeParameterListExpression(
mandatoryTypeParameters, mandatoryTypeParameters,
typeParameterMap, typeParameterMap,
callableInfo.kind != CallableKind.CONSTRUCTOR callableInfo.kind != CallableKind.CLASS_WITH_PRIMARY_CONSTRUCTOR
) )
builder.replaceElement(typeParameterList, expression, false) builder.replaceElement(typeParameterList, expression, false)
return expression return expression
@@ -831,8 +850,8 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
} }
return when (adjustedDeclaration) { return when (adjustedDeclaration) {
is JetNamedFunction -> { is JetNamedFunction, is JetSecondaryConstructor -> {
createJavaMethod(adjustedDeclaration, targetClass) createJavaMethod(adjustedDeclaration as JetFunction, targetClass)
} }
is JetProperty -> { is JetProperty -> {
createJavaField(adjustedDeclaration, targetClass) createJavaField(adjustedDeclaration, targetClass)
@@ -859,7 +878,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
} }
val needStatic = when (callableInfo) { val needStatic = when (callableInfo) {
is ConstructorInfo -> with(callableInfo.classInfo) { is PrimaryConstructorInfo -> with(callableInfo.classInfo) {
!inner && kind != ClassKind.ENUM_ENTRY && kind != ClassKind.ENUM_CLASS !inner && kind != ClassKind.ENUM_ENTRY && kind != ClassKind.ENUM_CLASS
} }
else -> callableInfo.receiverTypeInfo.staticContextRequired else -> callableInfo.receiverTypeInfo.staticContextRequired
@@ -896,7 +915,12 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
val caretModel = containingFileEditor.getCaretModel() val caretModel = containingFileEditor.getCaretModel()
val selectionModel = containingFileEditor.getSelectionModel() val selectionModel = containingFileEditor.getSelectionModel()
caretModel.moveToOffset(declaration.getNameIdentifier().getTextRange().getEndOffset()) if (declaration is JetSecondaryConstructor) {
caretModel.moveToOffset(declaration.getConstructorKeyword().getTextRange().getEndOffset())
}
else {
caretModel.moveToOffset(declaration.getNameIdentifier()!!.getTextRange().getEndOffset())
}
fun positionBetween(left: PsiElement, right: PsiElement) { fun positionBetween(left: PsiElement, right: PsiElement) {
val from = left.siblings(withItself = false, forward = true).firstOrNull { it !is PsiWhiteSpace } ?: return val from = left.siblings(withItself = false, forward = true).firstOrNull { it !is PsiWhiteSpace } ?: return
@@ -908,8 +932,10 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
} }
when (declaration) { when (declaration) {
is JetNamedFunction -> { is JetNamedFunction, is JetSecondaryConstructor -> {
(declaration.getBodyExpression() as? JetBlockExpression)?.let { positionBetween(it.getLBrace(), it.getRBrace()) } ((declaration as JetFunction).getBodyExpression() as? JetBlockExpression)?.let {
positionBetween(it.getLBrace(), it.getRBrace())
}
} }
is JetClassOrObject -> { is JetClassOrObject -> {
caretModel.moveToOffset(declaration.getTextRange().getStartOffset()) caretModel.moveToOffset(declaration.getTextRange().getStartOffset())
@@ -995,8 +1021,8 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
runWriteAction { runWriteAction {
// file templates // file templates
if (newDeclaration is JetNamedFunction) { if (newDeclaration is JetNamedFunction || newDeclaration is JetSecondaryConstructor) {
setupFunctionBody(newDeclaration) setupFunctionBody(newDeclaration as JetFunction)
} }
val callElement = config.originalElement as? JetCallElement val callElement = config.originalElement as? JetCallElement
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder package org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder
import com.intellij.psi.PsiElement
import java.util.Collections import java.util.Collections
import org.jetbrains.kotlin.psi.JetExpression import org.jetbrains.kotlin.psi.JetExpression
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.Variance
@@ -119,7 +120,8 @@ class ParameterInfo(
enum class CallableKind { enum class CallableKind {
FUNCTION FUNCTION
CONSTRUCTOR CLASS_WITH_PRIMARY_CONSTRUCTOR
SECONDARY_CONSTRUCTOR
PROPERTY PROPERTY
} }
@@ -144,13 +146,20 @@ class FunctionInfo(name: String,
override val kind: CallableKind get() = CallableKind.FUNCTION override val kind: CallableKind get() = CallableKind.FUNCTION
} }
class ConstructorInfo(val classInfo: ClassInfo, expectedTypeInfo: TypeInfo): CallableInfo( class PrimaryConstructorInfo(val classInfo: ClassInfo, expectedTypeInfo: TypeInfo): CallableInfo(
classInfo.name, TypeInfo.Empty, expectedTypeInfo.forceNotNull(), Collections.emptyList(), classInfo.typeArguments classInfo.name, TypeInfo.Empty, expectedTypeInfo.forceNotNull(), Collections.emptyList(), classInfo.typeArguments
) { ) {
override val kind: CallableKind get() = CallableKind.CONSTRUCTOR override val kind: CallableKind get() = CallableKind.CLASS_WITH_PRIMARY_CONSTRUCTOR
override val parameterInfos: List<ParameterInfo> get() = classInfo.parameterInfos override val parameterInfos: List<ParameterInfo> get() = classInfo.parameterInfos
} }
class SecondaryConstructorInfo(
override val parameterInfos: List<ParameterInfo>,
val targetClass: PsiElement
): CallableInfo("", TypeInfo.Empty, TypeInfo.Empty, Collections.emptyList(), Collections.emptyList()) {
override val kind: CallableKind get() = CallableKind.SECONDARY_CONSTRUCTOR
}
class PropertyInfo(name: String, class PropertyInfo(name: String,
receiverTypeInfo: TypeInfo, receiverTypeInfo: TypeInfo,
returnTypeInfo: TypeInfo, returnTypeInfo: TypeInfo,
@@ -16,25 +16,20 @@
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.CreateFromUsageFixBase
import org.jetbrains.kotlin.psi.JetFile
import com.intellij.openapi.editor.Editor
import org.jetbrains.kotlin.idea.JetBundle
import com.intellij.openapi.project.Project
import com.intellij.openapi.command.CommandProcessor import com.intellij.openapi.command.CommandProcessor
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import com.intellij.openapi.editor.Editor
import org.jetbrains.kotlin.psi.JetClassOrObject import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.idea.refactoring.chooseContainerElementIfNecessary
import org.jetbrains.kotlin.psi.JetClassBody
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.*
import org.jetbrains.kotlin.psi.JetExpression
import java.util.HashSet
import org.jetbrains.kotlin.psi.JetElement
import com.intellij.psi.PsiClass import com.intellij.psi.PsiClass
import java.util.Collections
import org.jetbrains.kotlin.psi.JetPsiUtil
import org.jetbrains.kotlin.idea.refactoring.canRefactor
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.JetBundle
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.CreateFromUsageFixBase
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.*
import org.jetbrains.kotlin.idea.refactoring.canRefactor
import org.jetbrains.kotlin.idea.refactoring.chooseContainerElementIfNecessary
import org.jetbrains.kotlin.psi.*
import java.util.Collections
import java.util.HashSet
public class CreateCallableFromUsageFix( public class CreateCallableFromUsageFix(
originalExpression: JetExpression, originalExpression: JetExpression,
@@ -64,9 +59,10 @@ public class CreateCallableFromUsageFix(
val kind = when (it.kind) { val kind = when (it.kind) {
CallableKind.FUNCTION -> "function" CallableKind.FUNCTION -> "function"
CallableKind.PROPERTY -> "property" CallableKind.PROPERTY -> "property"
CallableKind.SECONDARY_CONSTRUCTOR -> "secondary constructor"
else -> throw AssertionError("Unexpected callable info: $it") else -> throw AssertionError("Unexpected callable info: $it")
} }
"$kind '${it.name}'" if (it.name.isNotEmpty()) "$kind '${it.name}'" else kind
} }
return JetBundle.message( return JetBundle.message(
"create.0.from.usage", "create.0.from.usage",
@@ -114,6 +110,11 @@ public class CreateCallableFromUsageFix(
CommandProcessor.getInstance().executeCommand(project, { callableBuilder.build() }, getText(), null) CommandProcessor.getInstance().executeCommand(project, { callableBuilder.build() }, getText(), null)
} }
if (callableInfo is SecondaryConstructorInfo) {
runBuilder(CallablePlacement.NoReceiver(callableInfo.targetClass))
return
}
val popupTitle = JetBundle.message("choose.target.class.or.trait.title") val popupTitle = JetBundle.message("choose.target.class.or.trait.title")
val receiverTypeCandidates = callableBuilder.computeTypeCandidates(callableInfo.receiverTypeInfo) val receiverTypeCandidates = callableBuilder.computeTypeCandidates(callableInfo.receiverTypeInfo)
if (receiverTypeCandidates.isNotEmpty()) { if (receiverTypeCandidates.isNotEmpty()) {
@@ -26,14 +26,8 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiDirectory import com.intellij.psi.PsiDirectory
import org.jetbrains.kotlin.idea.JetFileType import org.jetbrains.kotlin.idea.JetFileType
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ParameterInfo
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ConstructorInfo
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableBuilderConfiguration
import java.util.Collections import java.util.Collections
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.createBuilder
import com.intellij.openapi.command.CommandProcessor import com.intellij.openapi.command.CommandProcessor
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallablePlacement
import org.jetbrains.kotlin.idea.refactoring.getOrCreateKotlinFile import org.jetbrains.kotlin.idea.refactoring.getOrCreateKotlinFile
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.ClassKind.* import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.ClassKind.*
import com.intellij.psi.PsiPackage import com.intellij.psi.PsiPackage
@@ -45,6 +39,7 @@ import org.jetbrains.kotlin.idea.refactoring.canRefactor
import com.intellij.psi.PsiMember import com.intellij.psi.PsiMember
import com.intellij.psi.PsiClass import com.intellij.psi.PsiClass
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.*
enum class ClassKind(val keyword: String, val description: String) { enum class ClassKind(val keyword: String, val description: String) {
PLAIN_CLASS: ClassKind("class", "class") PLAIN_CLASS: ClassKind("class", "class")
@@ -124,7 +119,7 @@ public class CreateClassFromUsageFix(
else -> throw AssertionError("Unexpected element: " + targetParent.getText()) else -> throw AssertionError("Unexpected element: " + targetParent.getText())
} ?: return } ?: return
val constructorInfo = ConstructorInfo(classInfo, expectedTypeInfo) val constructorInfo = PrimaryConstructorInfo(classInfo, expectedTypeInfo)
val builder = CallableBuilderConfiguration( val builder = CallableBuilderConfiguration(
Collections.singletonList(constructorInfo), element as JetElement, file, editor, false, kind == PLAIN_CLASS || kind == TRAIT Collections.singletonList(constructorInfo), element as JetElement, file, editor, false, kind == PLAIN_CLASS || kind == TRAIT
).createBuilder() ).createBuilder()
@@ -481,7 +481,7 @@ private fun copyTypeParameters<T: PsiTypeParameterListOwner>(
} }
} }
public fun createJavaMethod(function: JetNamedFunction, targetClass: PsiClass): PsiMethod { public fun createJavaMethod(function: JetFunction, targetClass: PsiClass): PsiMethod {
val template = LightClassUtil.getLightClassMethod(function) val template = LightClassUtil.getLightClassMethod(function)
?: throw AssertionError("Can't generate light method: ${JetPsiUtil.getElementTextWithContext(function)}") ?: throw AssertionError("Can't generate light method: ${JetPsiUtil.getElementTextWithContext(function)}")
return createJavaMethod(template, targetClass) return createJavaMethod(template, targetClass)