Callable Builder: Support class generation
This commit is contained in:
@@ -228,6 +228,10 @@ public class JetPsiFactory(private val project: Project) {
|
||||
return createFunction("fun foo$text{}").getValueParameterList()!!
|
||||
}
|
||||
|
||||
public fun createEnumEntry(text: String): JetEnumEntry {
|
||||
return createDeclaration<JetClass>("enum class E {$text}").getDeclarations()[0] as JetEnumEntry
|
||||
}
|
||||
|
||||
public fun createWhenEntry(entryText: String): JetWhenEntry {
|
||||
val function = createFunction("fun foo() { when(12) { " + entryText + " } }")
|
||||
val whenEntry = PsiTreeUtil.findChildOfType(function, javaClass<JetWhenEntry>())
|
||||
|
||||
@@ -36,6 +36,7 @@ fun JetType.makeNotNullable() = TypeUtils.makeNotNullable(this)
|
||||
fun JetType.supertypes(): Set<JetType> = TypeUtils.getAllSupertypes(this)
|
||||
|
||||
fun JetType.isUnit(): Boolean = KotlinBuiltIns.getInstance().isUnit(this)
|
||||
fun JetType.isAny(): Boolean = KotlinBuiltIns.getInstance().isAnyOrNullableAny(this)
|
||||
|
||||
public fun approximateFlexibleTypes(jetType: JetType, outermost: Boolean = true): JetType {
|
||||
if (jetType.isFlexible()) {
|
||||
|
||||
+151
-67
@@ -19,14 +19,12 @@ package org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder
|
||||
import com.intellij.codeInsight.navigation.NavigationUtil
|
||||
import com.intellij.codeInsight.template.*
|
||||
import com.intellij.codeInsight.template.impl.TemplateImpl
|
||||
import com.intellij.codeInsight.template.impl.Variable
|
||||
import com.intellij.ide.fileTemplates.FileTemplate
|
||||
import com.intellij.ide.fileTemplates.FileTemplateManager
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.progress.ProcessCanceledException
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.util.IncorrectOperationException
|
||||
@@ -65,6 +63,8 @@ import org.jetbrains.jet.lang.descriptors.impl.MutablePackageFragmentDescriptor
|
||||
import org.jetbrains.jet.lang.descriptors.impl.TypeParameterDescriptorImpl
|
||||
import java.util.LinkedHashMap
|
||||
import org.jetbrains.jet.plugin.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.jet.plugin.quickfix.createFromUsage.createClass.ClassKind
|
||||
import org.jetbrains.jet.plugin.util.isAny
|
||||
import org.jetbrains.jet.utils.addToStdlib.singletonOrEmptyList
|
||||
|
||||
private val TYPE_PARAMETER_LIST_VARIABLE_NAME = "typeParameterList"
|
||||
@@ -113,7 +113,7 @@ fun List<TypeCandidate>.getTypeByRenderedType(renderedType: String): JetType? =
|
||||
|
||||
class CallableBuilderConfiguration(
|
||||
val callableInfos: List<CallableInfo>,
|
||||
val originalExpression: JetExpression,
|
||||
val originalElement: JetElement,
|
||||
val currentFile: JetFile,
|
||||
val currentEditor: Editor,
|
||||
val enableSubstitutions: Boolean = true
|
||||
@@ -285,10 +285,14 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
||||
computeTypeCandidates(it.typeInfo, substitutions, scope)
|
||||
}
|
||||
|
||||
val returnTypeCandidates = computeTypeCandidates(callableInfo.returnTypeInfo, substitutions, scope)
|
||||
skipReturnType = callableInfo is FunctionInfo
|
||||
&& returnTypeCandidates.size == 1
|
||||
&& returnTypeCandidates.first().theType.isUnit()
|
||||
val returnTypeCandidate = computeTypeCandidates(callableInfo.returnTypeInfo, substitutions, scope).singleOrNull()
|
||||
skipReturnType = when (callableInfo.kind) {
|
||||
CallableKind.FUNCTION ->
|
||||
returnTypeCandidate?.theType?.isUnit() ?: false
|
||||
CallableKind.CONSTRUCTOR ->
|
||||
callableInfo.returnTypeInfo == TypeInfo.Empty || returnTypeCandidate?.theType?.isAny() ?: false
|
||||
CallableKind.PROPERTY -> false
|
||||
}
|
||||
|
||||
// figure out type parameter renames to avoid conflicts
|
||||
typeParameterNameMap = getTypeParameterRenames(scope)
|
||||
@@ -355,31 +359,63 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
||||
typeCandidates[typeInfo]?.forEach { it.render(typeParameterNameMap, fakeFunction) }
|
||||
}
|
||||
|
||||
private fun createDeclarationSkeleton(): JetCallableDeclaration {
|
||||
private fun createDeclarationSkeleton(): JetNamedDeclaration {
|
||||
with (config) {
|
||||
val assignmentToReplace =
|
||||
if (containingElement is JetBlockExpression && (callableInfo as? PropertyInfo)?.writable ?: false) {
|
||||
originalExpression as JetBinaryExpression
|
||||
originalElement as JetBinaryExpression
|
||||
}
|
||||
else null
|
||||
|
||||
val ownerTypeString = if (isExtension) "${receiverTypeCandidate!!.renderedType!!}." else ""
|
||||
|
||||
val classKind = (callableInfo as? ConstructorInfo)?.classInfo?.kind
|
||||
|
||||
fun renderParamList(): String {
|
||||
val prefix = if (classKind == ClassKind.ANNOTATION_CLASS) "val " else ""
|
||||
val list = callableInfo.parameterInfos.indices.map { i -> "${prefix}p$i: Any" }.joinToString(", ")
|
||||
return if (callableInfo.parameterInfos.isNotEmpty() || callableInfo.kind == CallableKind.FUNCTION) "($list)" else list
|
||||
}
|
||||
|
||||
val paramList = when (callableInfo.kind) {
|
||||
CallableKind.FUNCTION ->
|
||||
"(${(callableInfo as FunctionInfo).parameterInfos.indices.map { i -> "p$i: Any" }.joinToString(", ")})"
|
||||
CallableKind.PROPERTY ->
|
||||
""
|
||||
CallableKind.FUNCTION, CallableKind.CONSTRUCTOR -> renderParamList()
|
||||
CallableKind.PROPERTY -> ""
|
||||
}
|
||||
val returnTypeString = if (skipReturnType || assignmentToReplace != null) "" else ": Any"
|
||||
val header = "$ownerTypeString${callableInfo.name}$paramList$returnTypeString"
|
||||
|
||||
val psiFactory = JetPsiFactory(currentFile)
|
||||
|
||||
val declaration : PsiElement = when (callableInfo.kind) {
|
||||
CallableKind.FUNCTION -> psiFactory.createFunction("fun $header {}")
|
||||
val declaration : JetNamedDeclaration = when (callableInfo.kind) {
|
||||
CallableKind.FUNCTION -> psiFactory.createFunction("fun<> $header {}")
|
||||
CallableKind.CONSTRUCTOR -> {
|
||||
with((callableInfo as ConstructorInfo).classInfo) {
|
||||
val classBody = when (kind) {
|
||||
ClassKind.ANNOTATION_CLASS, ClassKind.ENUM_ENTRY -> ""
|
||||
else -> "{\n\n}"
|
||||
}
|
||||
when (kind) {
|
||||
ClassKind.ENUM_ENTRY -> {
|
||||
if (!(targetParent is JetClass && targetParent.isEnum())) throw AssertionError("Enum class expected: ${targetParent.getText()}")
|
||||
val hasParameters = targetParent.getPrimaryConstructorParameters().isNotEmpty()
|
||||
psiFactory.createEnumEntry("$name ${if (hasParameters) ": ${targetParent.getName()}()" else ""}")
|
||||
}
|
||||
else -> {
|
||||
val innerMod = if (inner) "inner " else ""
|
||||
val typeParamList = when (kind) {
|
||||
ClassKind.PLAIN_CLASS, ClassKind.TRAIT -> "<>"
|
||||
else -> ""
|
||||
}
|
||||
psiFactory.createDeclaration<JetClassOrObject>(
|
||||
"$innerMod${kind.keyword} $name$typeParamList$paramList$returnTypeString $classBody"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CallableKind.PROPERTY -> {
|
||||
val valVar = if ((callableInfo as PropertyInfo).writable) "var" else "val"
|
||||
psiFactory.createProperty("$valVar $header")
|
||||
psiFactory.createProperty("$valVar<> $header")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,7 +459,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
||||
}
|
||||
|
||||
when (containingElement) {
|
||||
is JetFile -> return append(declaration, containingElement.getLastChild()!!, false) as JetCallableDeclaration
|
||||
is JetFile -> return append(declaration, containingElement.getLastChild()!!, false) as JetNamedDeclaration
|
||||
|
||||
is JetClassOrObject -> {
|
||||
var classBody = containingElement.getBody()
|
||||
@@ -435,9 +471,9 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
||||
if (declaration is JetNamedFunction) {
|
||||
val rBrace = classBody!!.getRBrace()
|
||||
return (rBrace?.let { append(declaration, it, true) }
|
||||
?: append(declaration, classBody!!.getLastChild()!!, false)) as JetCallableDeclaration
|
||||
?: append(declaration, classBody!!.getLastChild()!!, false)) as JetNamedDeclaration
|
||||
}
|
||||
return prepend(declaration, classBody!!.getLBrace()!!, true) as JetCallableDeclaration
|
||||
return prepend(declaration, classBody!!.getLBrace()!!, true) as JetNamedDeclaration
|
||||
}
|
||||
|
||||
is JetBlockExpression -> {
|
||||
@@ -448,9 +484,9 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
||||
parent.addAfter(newLine, containingElement)
|
||||
}
|
||||
|
||||
return prepend(declaration, containingElement.getFirstChild()!!, false) as JetCallableDeclaration
|
||||
return prepend(declaration, containingElement.getFirstChild()!!, false) as JetNamedDeclaration
|
||||
}
|
||||
return prepend(declaration, containingElement.getLBrace()!!, true) as JetCallableDeclaration
|
||||
return prepend(declaration, containingElement.getLBrace()!!, true) as JetNamedDeclaration
|
||||
}
|
||||
|
||||
else -> throw AssertionError("Invalid containing element: ${containingElement.getText()}")
|
||||
@@ -476,20 +512,20 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
||||
return allTypeParametersNotInScope.zip(typeParameterNames).toMap()
|
||||
}
|
||||
|
||||
private fun setupTypeReferencesForShortening(declaration: JetCallableDeclaration,
|
||||
private fun setupTypeReferencesForShortening(declaration: JetNamedDeclaration,
|
||||
typeRefsToShorten: MutableList<JetElement>,
|
||||
parameterTypeExpressions: List<TypeExpression>) {
|
||||
if (isExtension) {
|
||||
val receiverTypeRef = JetPsiFactory(declaration).createType(receiverTypeCandidate!!.theType.renderLong(typeParameterNameMap))
|
||||
replaceWithLongerName(receiverTypeRef, receiverTypeCandidate.theType)
|
||||
|
||||
val funcReceiverTypeRef = declaration.getReceiverTypeReference()
|
||||
val funcReceiverTypeRef = (declaration as? JetCallableDeclaration)?.getReceiverTypeReference()
|
||||
if (funcReceiverTypeRef != null) {
|
||||
typeRefsToShorten.add(funcReceiverTypeRef)
|
||||
}
|
||||
}
|
||||
|
||||
val returnTypeRef = declaration.getTypeReference()
|
||||
val returnTypeRef = declaration.getReturnTypeReference()
|
||||
if (returnTypeRef != null) {
|
||||
val returnType = typeCandidates[callableInfo.returnTypeInfo]!!.getTypeByRenderedType(
|
||||
returnTypeRef.getText()
|
||||
@@ -498,11 +534,11 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
||||
if (returnType != null) {
|
||||
// user selected a given type
|
||||
replaceWithLongerName(returnTypeRef, returnType)
|
||||
typeRefsToShorten.add(declaration.getTypeReference()!!)
|
||||
typeRefsToShorten.add(declaration.getReturnTypeReference()!!)
|
||||
}
|
||||
}
|
||||
|
||||
val valueParameters = declaration.getValueParameterList()?.getParameters() ?: Collections.emptyList<JetParameter>()
|
||||
val valueParameters = declaration.getValueParameters()
|
||||
val parameterIndicesToShorten = ArrayList<Int>()
|
||||
assert(valueParameters.size == parameterTypeExpressions.size)
|
||||
for ((i, parameter) in valueParameters.stream().withIndices()) {
|
||||
@@ -519,11 +555,10 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
||||
}
|
||||
}
|
||||
|
||||
declaration.getValueParameterList()?.getParameters()?.let { expandedValueParameters ->
|
||||
parameterIndicesToShorten.stream()
|
||||
.map { expandedValueParameters[it].getTypeReference() }
|
||||
.filterNotNullTo(typeRefsToShorten)
|
||||
}
|
||||
val expandedValueParameters = declaration.getValueParameters()
|
||||
parameterIndicesToShorten.stream()
|
||||
.map { expandedValueParameters[it].getTypeReference() }
|
||||
.filterNotNullTo(typeRefsToShorten)
|
||||
}
|
||||
|
||||
private fun setupFunctionBody(func: JetNamedFunction) {
|
||||
@@ -552,33 +587,47 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
||||
func.getBodyExpression()!!.replace(newBodyExpression)
|
||||
}
|
||||
|
||||
private fun setupCallTypeArguments(callExpr: JetCallExpression, typeParameters: List<TypeParameterDescriptor>) {
|
||||
val oldTypeArgumentList = callExpr.getTypeArgumentList() ?: return
|
||||
private fun setupCallTypeArguments(callElement: JetCallElement, typeParameters: List<TypeParameterDescriptor>) {
|
||||
val oldTypeArgumentList = callElement.getTypeArgumentList() ?: return
|
||||
val renderedTypeArgs = typeParameters.map { typeParameter ->
|
||||
val type = substitutions.first { it.byType.getConstructor().getDeclarationDescriptor() == typeParameter }.forType
|
||||
IdeDescriptorRenderers.SOURCE_CODE.renderType(type)
|
||||
}
|
||||
oldTypeArgumentList.replace(JetPsiFactory(callExpr).createTypeArguments(renderedTypeArgs.joinToString(", ", "<", ">")))
|
||||
elementsToShorten.add(callExpr.getTypeArgumentList())
|
||||
if (renderedTypeArgs.isEmpty()) {
|
||||
oldTypeArgumentList.delete()
|
||||
}
|
||||
else {
|
||||
oldTypeArgumentList.replace(JetPsiFactory(callElement).createTypeArguments(renderedTypeArgs.joinToString(", ", "<", ">")))
|
||||
elementsToShorten.add(callElement.getTypeArgumentList())
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupReturnTypeTemplate(builder: TemplateBuilder, declaration: JetCallableDeclaration): TypeExpression? {
|
||||
val returnTypeRef = declaration.getTypeReference() ?: return null
|
||||
private fun setupReturnTypeTemplate(builder: TemplateBuilder, declaration: JetNamedDeclaration): TypeExpression? {
|
||||
val candidates = typeCandidates[callableInfo.returnTypeInfo]!!
|
||||
return when (candidates.size) {
|
||||
0 -> null
|
||||
if (candidates.isEmpty()) return null
|
||||
|
||||
1 -> {
|
||||
builder.replaceElement(returnTypeRef, candidates.first().renderedType!!)
|
||||
null
|
||||
val elementToReplace: JetElement?
|
||||
val expression: TypeExpression
|
||||
when (declaration) {
|
||||
is JetCallableDeclaration -> {
|
||||
elementToReplace = declaration.getTypeReference()
|
||||
expression = TypeExpression.ForTypeReference(candidates)
|
||||
}
|
||||
|
||||
else -> {
|
||||
val returnTypeExpression = TypeExpression(candidates)
|
||||
builder.replaceElement(returnTypeRef, returnTypeExpression)
|
||||
returnTypeExpression
|
||||
is JetClassOrObject -> {
|
||||
elementToReplace = declaration.getDelegationSpecifiers().firstOrNull()
|
||||
expression = TypeExpression.ForDelegationSpecifier(candidates)
|
||||
}
|
||||
else -> throw AssertionError("Unexpected declaration kind: ${declaration.getText()}")
|
||||
}
|
||||
if (elementToReplace == null) return null
|
||||
|
||||
if (candidates.size == 1) {
|
||||
builder.replaceElement(elementToReplace, (expression.calculateResult(null) as TextResult).getText())
|
||||
return null
|
||||
}
|
||||
|
||||
builder.replaceElement(elementToReplace, expression)
|
||||
return expression
|
||||
}
|
||||
|
||||
private fun setupValVarTemplate(builder: TemplateBuilder, property: JetProperty) {
|
||||
@@ -587,7 +636,17 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupTypeParameterListTemplate(builder: TemplateBuilderImpl, declaration: JetCallableDeclaration): TypeParameterListExpression {
|
||||
private fun setupTypeParameterListTemplate(
|
||||
builder: TemplateBuilderImpl,
|
||||
declaration: JetNamedDeclaration
|
||||
): TypeParameterListExpression? {
|
||||
when (declaration) {
|
||||
is JetObjectDeclaration -> return null
|
||||
!is JetTypeParameterListOwner -> throw AssertionError("Unexpected declaration kind: ${declaration.getText()}")
|
||||
}
|
||||
|
||||
val typeParameterList = (declaration as JetTypeParameterListOwner).getTypeParameterList() ?: return null
|
||||
|
||||
val typeParameterMap = HashMap<String, List<RenderedTypeParameter>>()
|
||||
|
||||
val mandatoryTypeParameters = ArrayList<RenderedTypeParameter>()
|
||||
@@ -598,14 +657,19 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
||||
typeParameterMap[it.renderedType!!] = it.renderedTypeParameters!!
|
||||
}
|
||||
|
||||
if (declaration.getTypeReference() != null) {
|
||||
if (declaration.getReturnTypeReference() != null) {
|
||||
typeCandidates[callableInfo.returnTypeInfo]!!.forEach {
|
||||
typeParameterMap[it.renderedType!!] = it.renderedTypeParameters!!
|
||||
}
|
||||
}
|
||||
// ((3, 3) is after "fun")
|
||||
builder.replaceElement(declaration, TextRange.create(3, 3), TYPE_PARAMETER_LIST_VARIABLE_NAME, null, false)
|
||||
return TypeParameterListExpression(mandatoryTypeParameters, typeParameterMap)
|
||||
|
||||
val expression = TypeParameterListExpression(
|
||||
mandatoryTypeParameters,
|
||||
typeParameterMap,
|
||||
callableInfo.kind != CallableKind.CONSTRUCTOR
|
||||
)
|
||||
builder.replaceElement(typeParameterList, expression, false)
|
||||
return expression
|
||||
}
|
||||
|
||||
private fun setupParameterTypeTemplates(builder: TemplateBuilder, parameterList: List<JetParameter>): List<TypeExpression> {
|
||||
@@ -613,7 +677,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
||||
|
||||
val typeParameters = ArrayList<TypeExpression>()
|
||||
for ((parameter, jetParameter) in callableInfo.parameterInfos.zip(parameterList)) {
|
||||
val parameterTypeExpression = TypeExpression(typeCandidates[parameter.typeInfo]!!)
|
||||
val parameterTypeExpression = TypeExpression.ForTypeReference(typeCandidates[parameter.typeInfo]!!)
|
||||
val parameterTypeRef = jetParameter.getTypeReference()!!
|
||||
builder.replaceElement(parameterTypeRef, parameterTypeExpression)
|
||||
|
||||
@@ -671,8 +735,8 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
||||
if (!skipReturnType) {
|
||||
setupReturnTypeTemplate(builder, declaration)
|
||||
}
|
||||
val parameterTypeExpressions =
|
||||
setupParameterTypeTemplates(builder, declaration.getValueParameterList()?.getParameters() ?: Collections.emptyList())
|
||||
|
||||
val parameterTypeExpressions = setupParameterTypeTemplates(builder, declaration.getValueParameters())
|
||||
|
||||
// add a segment for the parameter list
|
||||
// Note: because TemplateBuilderImpl does not have a replaceElement overload that takes in both a TextRange and alwaysStopAt, we
|
||||
@@ -684,13 +748,14 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
||||
// the template built by TemplateBuilderImpl is ordered by element position, but we want types to be first, so hack it
|
||||
val templateImpl = builder.buildInlineTemplate() as TemplateImpl
|
||||
val variables = templateImpl.getVariables()!!
|
||||
for (i in 0..(callableInfo.parameterInfos.size - 1)) {
|
||||
Collections.swap(variables, i * 2, i * 2 + 1)
|
||||
if (variables.isNotEmpty()) {
|
||||
val typeParametersVar = if (expression != null) variables.remove(0) else null
|
||||
for (i in 0..(callableInfo.parameterInfos.size - 1)) {
|
||||
Collections.swap(variables, i * 2, i * 2 + 1)
|
||||
}
|
||||
typeParametersVar?.let { variables.add(it) }
|
||||
}
|
||||
|
||||
// fix up the template to include the expression for the type parameter list
|
||||
variables.add(Variable(TYPE_PARAMETER_LIST_VARIABLE_NAME, expression, expression, false, true))
|
||||
|
||||
// TODO: Disabled shortening names because it causes some tests fail. Refactor code to use automatic reference shortening
|
||||
templateImpl.setToShortenLongNames(false)
|
||||
|
||||
@@ -700,18 +765,20 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
||||
PsiDocumentManager.getInstance(project).commitDocument(containingFileEditor.getDocument())
|
||||
|
||||
// file templates
|
||||
val offset = templateImpl.getSegmentOffset(0)
|
||||
val newDeclaration = PsiTreeUtil.findElementOfClassAtOffset(
|
||||
containingFile, offset, javaClass<JetCallableDeclaration>(), false
|
||||
)!!
|
||||
val newDeclaration = if (templateImpl.getSegmentsCount() > 0) {
|
||||
PsiTreeUtil.findElementOfClassAtOffset(containingFile, templateImpl.getSegmentOffset(0), declaration.javaClass, false)!!
|
||||
}
|
||||
else declarationPointer.getElement()!!
|
||||
|
||||
ApplicationManager.getApplication()!!.runWriteAction {
|
||||
// file templates
|
||||
if (newDeclaration is JetNamedFunction) {
|
||||
setupFunctionBody(newDeclaration)
|
||||
(config.originalExpression as? JetCallExpression)?.let {
|
||||
setupCallTypeArguments(it, expression.currentTypeParameters)
|
||||
}
|
||||
}
|
||||
|
||||
val callElement = config.originalElement as? JetCallElement
|
||||
if (callElement != null) {
|
||||
setupCallTypeArguments(callElement, expression?.currentTypeParameters ?: Collections.emptyList())
|
||||
}
|
||||
|
||||
// change short type names to fully qualified ones (to be shortened below)
|
||||
@@ -726,4 +793,21 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun JetNamedDeclaration.getValueParameters(): List<JetParameter> {
|
||||
return when (this) {
|
||||
is JetCallableDeclaration -> getValueParameterList()
|
||||
is JetClass -> getPrimaryConstructorParameterList()
|
||||
is JetObjectDeclaration -> null
|
||||
else -> throw AssertionError("Unexpected declaration kind: ${getText()}")
|
||||
}?.getParameters() ?: Collections.emptyList()
|
||||
}
|
||||
|
||||
private fun JetNamedDeclaration.getReturnTypeReference(): JetTypeReference? {
|
||||
return when (this) {
|
||||
is JetCallableDeclaration -> getTypeReference()
|
||||
is JetClassOrObject -> getDelegationSpecifiers().firstOrNull()?.getTypeReference()
|
||||
else -> throw AssertionError("Unexpected declaration kind: ${getText()}")
|
||||
}
|
||||
}
|
||||
|
||||
fun CallableBuilderConfiguration.createBuilder(): CallableBuilder = CallableBuilder(this)
|
||||
|
||||
+23
@@ -14,6 +14,12 @@ import org.jetbrains.jet.lang.types.ErrorUtils
|
||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
||||
import org.jetbrains.jet.lang.psi.JetElement
|
||||
import org.jetbrains.jet.lang.psi.JetTypeReference
|
||||
import org.jetbrains.jet.plugin.quickfix.createFromUsage.createClass.ClassKind
|
||||
import org.jetbrains.jet.plugin.quickfix.createFromUsage.createClass.ClassInfo
|
||||
import org.jetbrains.jet.plugin.util.makeNotNullable
|
||||
import org.jetbrains.jet.lang.types.TypeUtils
|
||||
import org.jetbrains.jet.lang.types.checker.JetTypeChecker
|
||||
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor
|
||||
|
||||
/**
|
||||
* Represents a concrete type or a set of types yet to be inferred from an expression.
|
||||
@@ -77,6 +83,15 @@ fun TypeInfo(theType: JetType, variance: Variance): TypeInfo = TypeInfo.ByType(t
|
||||
|
||||
fun TypeInfo.noSubstitutions(): TypeInfo = (this as? TypeInfo.NoSubstitutions) ?: TypeInfo.NoSubstitutions(this)
|
||||
|
||||
fun TypeInfo.forceNotNull(): TypeInfo {
|
||||
class ForcedNotNull(delegate: TypeInfo): TypeInfo.DelegatingTypeInfo(delegate) {
|
||||
override fun getPossibleTypes(builder: CallableBuilder): List<JetType> =
|
||||
super.getPossibleTypes(builder).map { it.makeNotNullable() }
|
||||
}
|
||||
|
||||
return (this as? ForcedNotNull) ?: ForcedNotNull(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates information about a function parameter that is going to be created.
|
||||
*/
|
||||
@@ -87,6 +102,7 @@ class ParameterInfo(
|
||||
|
||||
enum class CallableKind {
|
||||
FUNCTION
|
||||
CONSTRUCTOR
|
||||
PROPERTY
|
||||
}
|
||||
|
||||
@@ -111,6 +127,13 @@ class FunctionInfo(name: String,
|
||||
override val kind: CallableKind get() = CallableKind.FUNCTION
|
||||
}
|
||||
|
||||
class ConstructorInfo(val classInfo: ClassInfo, expectedTypeInfo: TypeInfo): CallableInfo(
|
||||
classInfo.name, TypeInfo.Empty, expectedTypeInfo.forceNotNull(), Collections.emptyList(), classInfo.typeArguments
|
||||
) {
|
||||
override val kind: CallableKind get() = CallableKind.CONSTRUCTOR
|
||||
override val parameterInfos: List<ParameterInfo> get() = classInfo.parameterInfos
|
||||
}
|
||||
|
||||
class PropertyInfo(name: String,
|
||||
receiverTypeInfo: TypeInfo,
|
||||
returnTypeInfo: TypeInfo,
|
||||
|
||||
+36
-12
@@ -19,6 +19,11 @@ import org.jetbrains.jet.lang.psi.JetCallableDeclaration
|
||||
import java.util.Collections
|
||||
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
|
||||
import org.jetbrains.jet.lang.descriptors.ClassKind
|
||||
import org.jetbrains.jet.lang.psi.JetClass
|
||||
import org.jetbrains.jet.lang.psi.JetParameterList
|
||||
import org.jetbrains.jet.lang.psi.JetNamedDeclaration
|
||||
|
||||
/**
|
||||
* Special <code>Expression</code> for parameter names based on its type.
|
||||
@@ -48,8 +53,12 @@ private class ParameterNameExpression(
|
||||
val editor = context.getEditor()!!
|
||||
val file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()) as JetFile
|
||||
val elementAt = file.findElementAt(offset)
|
||||
val func = PsiTreeUtil.getParentOfType(elementAt, javaClass<JetFunction>()) ?: return array<LookupElement>()
|
||||
val parameterList = func.getValueParameterList()!!
|
||||
val declaration = PsiTreeUtil.getParentOfType(elementAt, javaClass<JetFunction>(), javaClass<JetClass>()) ?: return array<LookupElement>()
|
||||
val parameterList = when (declaration) {
|
||||
is JetFunction -> declaration.getValueParameterList()
|
||||
is JetClass -> declaration.getPrimaryConstructorParameterList()
|
||||
else -> throw AssertionError("Unexpected declaration: ${declaration.getText()}")
|
||||
}
|
||||
|
||||
// add names based on selected type
|
||||
val parameter = PsiTreeUtil.getParentOfType(elementAt, javaClass<JetParameter>())
|
||||
@@ -80,11 +89,24 @@ private class ParameterNameExpression(
|
||||
}
|
||||
|
||||
/**
|
||||
* An <code>Expression</code> for type references.
|
||||
* An <code>Expression</code> for type references and delegation specifiers.
|
||||
*/
|
||||
private class TypeExpression(public val typeCandidates: List<TypeCandidate>) : Expression() {
|
||||
private val cachedLookupElements: Array<LookupElement> =
|
||||
typeCandidates.map { LookupElementBuilder.create(it, it.renderedType!!) }.copyToArray()
|
||||
private abstract class TypeExpression(public val typeCandidates: List<TypeCandidate>) : Expression() {
|
||||
class ForTypeReference(typeCandidates: List<TypeCandidate>) : TypeExpression(typeCandidates) {
|
||||
override val cachedLookupElements: Array<LookupElement> =
|
||||
typeCandidates.map { LookupElementBuilder.create(it, it.renderedType!!) }.copyToArray()
|
||||
}
|
||||
|
||||
class ForDelegationSpecifier(typeCandidates: List<TypeCandidate>) : TypeExpression(typeCandidates) {
|
||||
override val cachedLookupElements: Array<LookupElement> =
|
||||
typeCandidates.map {
|
||||
val descriptor = it.theType.getConstructor().getDeclarationDescriptor() as ClassDescriptor
|
||||
val text = it.renderedType!! + if (descriptor.getKind() == ClassKind.TRAIT) "" else "()"
|
||||
LookupElementBuilder.create(it, text)
|
||||
}.copyToArray()
|
||||
}
|
||||
|
||||
protected abstract val cachedLookupElements: Array<LookupElement>
|
||||
|
||||
override fun calculateResult(context: ExpressionContext?): Result {
|
||||
val lookupItems = calculateLookupItems(context)
|
||||
@@ -100,7 +122,10 @@ private class TypeExpression(public val typeCandidates: List<TypeCandidate>) : E
|
||||
* A sort-of dummy <code>Expression</code> for parameter lists, to allow us to update the parameter list as the user makes selections.
|
||||
*/
|
||||
private class TypeParameterListExpression(private val mandatoryTypeParameters: List<RenderedTypeParameter>,
|
||||
private val parameterTypeToTypeParameterNamesMap: Map<String, List<RenderedTypeParameter>>) : Expression() {
|
||||
private val parameterTypeToTypeParameterNamesMap: Map<String, List<RenderedTypeParameter>>,
|
||||
insertLeadingSpace: Boolean) : Expression() {
|
||||
private val prefix = if (insertLeadingSpace) " <" else "<"
|
||||
|
||||
public var currentTypeParameters: List<TypeParameterDescriptor> = Collections.emptyList()
|
||||
private set
|
||||
|
||||
@@ -113,12 +138,11 @@ private class TypeParameterListExpression(private val mandatoryTypeParameters: L
|
||||
val editor = context.getEditor()!!
|
||||
val file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()) as JetFile
|
||||
val elementAt = file.findElementAt(offset)
|
||||
val callable = PsiTreeUtil.getParentOfType(elementAt, javaClass<JetCallableDeclaration>()) ?: return TextResult("")
|
||||
val parameters = callable.getValueParameterList()?.getParameters() ?: Collections.emptyList<JetParameter>()
|
||||
val declaration = PsiTreeUtil.getParentOfType(elementAt, javaClass<JetNamedDeclaration>()) ?: return TextResult("")
|
||||
|
||||
val renderedTypeParameters = LinkedHashSet<RenderedTypeParameter>()
|
||||
renderedTypeParameters.addAll(mandatoryTypeParameters)
|
||||
for (parameter in parameters) {
|
||||
for (parameter in declaration.getValueParameters()) {
|
||||
val parameterTypeRef = parameter.getTypeReference()
|
||||
if (parameterTypeRef != null) {
|
||||
val typeParameterNamesFromParameter = parameterTypeToTypeParameterNamesMap[parameterTypeRef.getText()]
|
||||
@@ -127,7 +151,7 @@ private class TypeParameterListExpression(private val mandatoryTypeParameters: L
|
||||
}
|
||||
}
|
||||
}
|
||||
val returnTypeRef = callable.getTypeReference()
|
||||
val returnTypeRef = declaration.getReturnTypeReference()
|
||||
if (returnTypeRef != null) {
|
||||
val typeParameterNamesFromReturnType = parameterTypeToTypeParameterNamesMap[returnTypeRef.getText()]
|
||||
if (typeParameterNamesFromReturnType != null) {
|
||||
@@ -140,7 +164,7 @@ private class TypeParameterListExpression(private val mandatoryTypeParameters: L
|
||||
currentTypeParameters = sortedRenderedTypeParameters.map { it.typeParameter }
|
||||
|
||||
return TextResult(
|
||||
if (sortedRenderedTypeParameters.empty) "" else sortedRenderedTypeParameters.map { it.text }.joinToString(", ", " <", ">")
|
||||
if (sortedRenderedTypeParameters.empty) "" else sortedRenderedTypeParameters.map { it.text }.joinToString(", ", prefix, ">")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -83,10 +83,14 @@ fun JetType.getTypeParameters(): Set<TypeParameterDescriptor> {
|
||||
return typeParameters
|
||||
}
|
||||
|
||||
fun JetExpression.guessTypes(context: BindingContext, module: ModuleDescriptor?): Array<JetType> {
|
||||
fun JetExpression.guessTypes(
|
||||
context: BindingContext,
|
||||
module: ModuleDescriptor?,
|
||||
coerceUnusedToUnit: Boolean = true
|
||||
): Array<JetType> {
|
||||
val builtIns = KotlinBuiltIns.getInstance()
|
||||
|
||||
if (this !is JetDeclaration && isUsedAsStatement(context)) return array(builtIns.getUnitType())
|
||||
if (coerceUnusedToUnit && this !is JetDeclaration && isUsedAsStatement(context)) return array(builtIns.getUnitType())
|
||||
|
||||
// if we know the actual type of the expression
|
||||
val theType1 = context[BindingContext.EXPRESSION_TYPE, this]
|
||||
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2010-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.jet.plugin.quickfix.createFromUsage.createClass
|
||||
|
||||
import org.jetbrains.jet.plugin.quickfix.createFromUsage.CreateFromUsageFixBase
|
||||
import org.jetbrains.jet.lang.psi.JetElement
|
||||
import org.jetbrains.jet.plugin.JetBundle
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import org.jetbrains.jet.lang.psi.JetFile
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiDirectory
|
||||
import org.jetbrains.jet.plugin.JetFileType
|
||||
import org.jetbrains.jet.plugin.codeInsight.CodeInsightUtils
|
||||
import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.TypeInfo
|
||||
import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.ParameterInfo
|
||||
import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.ConstructorInfo
|
||||
import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.CallableBuilderConfiguration
|
||||
import org.jetbrains.jet.lang.psi.JetExpression
|
||||
import java.util.Collections
|
||||
import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.createBuilder
|
||||
import com.intellij.openapi.command.CommandProcessor
|
||||
import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.CallablePlacement
|
||||
import org.jetbrains.jet.plugin.refactoring.getOrCreateKotlinFile
|
||||
import org.jetbrains.jet.plugin.quickfix.createFromUsage.createClass.ClassKind.*
|
||||
|
||||
enum class ClassKind(val keyword: String, val description: String) {
|
||||
PLAIN_CLASS: ClassKind("class", "class")
|
||||
ENUM_CLASS: ClassKind("enum class", "enum")
|
||||
ENUM_ENTRY: ClassKind("", "enum constant")
|
||||
ANNOTATION_CLASS: ClassKind("annotation class", "annotation")
|
||||
TRAIT: ClassKind("trait", "trait")
|
||||
OBJECT: ClassKind("object", "object")
|
||||
}
|
||||
|
||||
public class ClassInfo(
|
||||
val kind: ClassKind,
|
||||
val name: String,
|
||||
val targetParent: PsiElement,
|
||||
val expectedTypeInfo: TypeInfo,
|
||||
val inner: Boolean = false,
|
||||
val typeArguments: List<TypeInfo> = Collections.emptyList(),
|
||||
val parameterInfos: List<ParameterInfo> = Collections.emptyList()
|
||||
)
|
||||
|
||||
public class CreateClassFromUsageFix(
|
||||
element: JetElement,
|
||||
val classInfo: ClassInfo
|
||||
): CreateFromUsageFixBase(element) {
|
||||
override fun getText(): String =
|
||||
JetBundle.message("create.0.from.usage", "${classInfo.kind.description} '${classInfo.name}'")
|
||||
|
||||
override fun invoke(project: Project, editor: Editor, file: JetFile) {
|
||||
with (classInfo) {
|
||||
val targetParent = when (targetParent) {
|
||||
is JetElement -> targetParent
|
||||
is PsiDirectory -> {
|
||||
val fileName = "$name.${JetFileType.INSTANCE.getDefaultExtension()}"
|
||||
val targetFile = getOrCreateKotlinFile(fileName, targetParent)
|
||||
if (targetFile == null) {
|
||||
val filePath = "${targetParent.getVirtualFile().getPath()}/$fileName"
|
||||
CodeInsightUtils.showErrorHint(
|
||||
targetParent.getProject(),
|
||||
editor,
|
||||
"File $filePath already exists but does not correspond to Kotlin file",
|
||||
"Create file",
|
||||
null
|
||||
)
|
||||
}
|
||||
targetFile
|
||||
}
|
||||
else -> throw AssertionError("Unexpected element: " + targetParent.getText())
|
||||
} as? JetElement ?: return
|
||||
|
||||
val constructorInfo = ConstructorInfo(classInfo, expectedTypeInfo)
|
||||
val builder = CallableBuilderConfiguration(
|
||||
Collections.singletonList(constructorInfo), element as JetElement, file, editor, kind == PLAIN_CLASS || kind == TRAIT
|
||||
).createBuilder()
|
||||
builder.placement = CallablePlacement.NoReceiver(targetParent)
|
||||
CommandProcessor.getInstance().executeCommand(project, { builder.build() }, getText(), null)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -36,6 +36,7 @@ public class CreateCallableFromUsageFix(
|
||||
val kind = when (it.kind) {
|
||||
CallableKind.FUNCTION -> "function"
|
||||
CallableKind.PROPERTY -> "property"
|
||||
else -> throw AssertionError("Unexpected callable info: $it")
|
||||
}
|
||||
"$kind '${it.name}'"
|
||||
}
|
||||
|
||||
@@ -80,6 +80,9 @@ fun <T: Any> PsiElement.getAndRemoveCopyableUserData(key: Key<T>): T? {
|
||||
return data
|
||||
}
|
||||
|
||||
fun getOrCreateKotlinFile(fileName: String, targetDir: PsiDirectory): JetFile? =
|
||||
(targetDir.findFile(fileName) ?: createKotlinFile(fileName, targetDir)) as? JetFile
|
||||
|
||||
fun createKotlinFile(fileName: String, targetDir: PsiDirectory): JetFile {
|
||||
val packageName = targetDir.getPackage()?.getQualifiedName()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user