Callable Builder: Support class generation

This commit is contained in:
Alexey Sedunov
2014-11-10 20:58:57 +03:00
parent aaf4283531
commit a76607beca
9 changed files with 322 additions and 81 deletions
@@ -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()) {
@@ -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)
@@ -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,
@@ -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, ">")
)
}
@@ -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]
@@ -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)
}
}
}
@@ -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()