Refactoring

This commit is contained in:
Dmitry Gridin
2019-02-26 16:14:49 +03:00
parent 76e133014b
commit 5d599ee2ff
@@ -78,9 +78,6 @@ import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny
import org.jetbrains.kotlin.types.typeUtil.isUnit import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.types.typeUtil.makeNullable import org.jetbrains.kotlin.types.typeUtil.makeNullable
import java.lang.AssertionError
import java.lang.IllegalArgumentException
import java.lang.IllegalStateException
import java.util.* import java.util.*
/** /**
@@ -96,7 +93,7 @@ class TypeCandidate(val theType: KotlinType, scope: HierarchicalScope? = null) {
fun render(typeParameterNameMap: Map<TypeParameterDescriptor, String>, fakeFunction: FunctionDescriptor?) { fun render(typeParameterNameMap: Map<TypeParameterDescriptor, String>, fakeFunction: FunctionDescriptor?) {
renderedTypes = theType.renderShort(typeParameterNameMap) renderedTypes = theType.renderShort(typeParameterNameMap)
renderedTypeParameters = typeParameters.map { renderedTypeParameters = typeParameters.map {
RenderedTypeParameter(it, it.containingDeclaration == fakeFunction, typeParameterNameMap[it]!!) RenderedTypeParameter(it, it.containingDeclaration == fakeFunction, typeParameterNameMap.getValue(it))
} }
} }
@@ -105,8 +102,7 @@ class TypeCandidate(val theType: KotlinType, scope: HierarchicalScope? = null) {
if (scope == null) { if (scope == null) {
typeParameters = typeParametersInType.toTypedArray() typeParameters = typeParametersInType.toTypedArray()
renderedTypes = theType.renderShort(Collections.emptyMap()) renderedTypes = theType.renderShort(Collections.emptyMap())
} } else {
else {
typeParameters = getTypeParameterNamesNotInScope(typeParametersInType, scope).toTypedArray() typeParameters = getTypeParameterNamesNotInScope(typeParametersInType, scope).toTypedArray()
} }
} }
@@ -115,26 +111,26 @@ class TypeCandidate(val theType: KotlinType, scope: HierarchicalScope? = null) {
} }
data class RenderedTypeParameter( data class RenderedTypeParameter(
val typeParameter: TypeParameterDescriptor, val typeParameter: TypeParameterDescriptor,
val fake: Boolean, val fake: Boolean,
val text: String val text: String
) )
fun List<TypeCandidate>.getTypeByRenderedType(renderedTypes: List<String>): KotlinType? = fun List<TypeCandidate>.getTypeByRenderedType(renderedTypes: List<String>): KotlinType? =
firstOrNull { it.renderedTypes == renderedTypes }?.theType firstOrNull { it.renderedTypes == renderedTypes }?.theType
class CallableBuilderConfiguration( class CallableBuilderConfiguration(
val callableInfos: List<CallableInfo>, val callableInfos: List<CallableInfo>,
val originalElement: KtElement, val originalElement: KtElement,
val currentFile: KtFile = originalElement.containingKtFile, val currentFile: KtFile = originalElement.containingKtFile,
val currentEditor: Editor? = null, val currentEditor: Editor? = null,
val isExtension: Boolean = false, val isExtension: Boolean = false,
val enableSubstitutions: Boolean = true val enableSubstitutions: Boolean = true
) )
sealed class CallablePlacement { sealed class CallablePlacement {
class WithReceiver(val receiverTypeCandidate: TypeCandidate): CallablePlacement() class WithReceiver(val receiverTypeCandidate: TypeCandidate) : CallablePlacement()
class NoReceiver(val containingElement: PsiElement): CallablePlacement() class NoReceiver(val containingElement: PsiElement) : CallablePlacement()
} }
class CallableBuilder(val config: CallableBuilderConfiguration) { class CallableBuilder(val config: CallableBuilderConfiguration) {
@@ -168,12 +164,13 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
} }
fun computeTypeCandidates(typeInfo: TypeInfo): List<TypeCandidate> = fun computeTypeCandidates(typeInfo: TypeInfo): List<TypeCandidate> =
typeCandidates.getOrPut(typeInfo) { typeInfo.getPossibleTypes(this).map { TypeCandidate(it) } } typeCandidates.getOrPut(typeInfo) { typeInfo.getPossibleTypes(this).map { TypeCandidate(it) } }
private fun computeTypeCandidates( private fun computeTypeCandidates(
typeInfo: TypeInfo, typeInfo: TypeInfo,
substitutions: List<KotlinTypeSubstitution>, substitutions: List<KotlinTypeSubstitution>,
scope: HierarchicalScope): List<TypeCandidate> { scope: HierarchicalScope
): List<TypeCandidate> {
if (!typeInfo.substitutionsAllowed) return computeTypeCandidates(typeInfo) if (!typeInfo.substitutionsAllowed) return computeTypeCandidates(typeInfo)
return typeCandidates.getOrPut(typeInfo) { return typeCandidates.getOrPut(typeInfo) {
val types = typeInfo.getPossibleTypes(this).asReversed() val types = typeInfo.getPossibleTypes(this).asReversed()
@@ -181,7 +178,8 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
// We have to use semantic equality here // We have to use semantic equality here
data class EqWrapper(val _type: KotlinType) { data class EqWrapper(val _type: KotlinType) {
override fun equals(other: Any?) = this === other override fun equals(other: Any?) = this === other
|| other is EqWrapper && KotlinTypeChecker.DEFAULT.equalTypes(_type, other._type) || other is EqWrapper && KotlinTypeChecker.DEFAULT.equalTypes(_type, other._type)
override fun hashCode() = 0 // no good way to compute hashCode() that would agree with our equals() override fun hashCode() = 0 // no good way to compute hashCode() that would agree with our equals()
} }
@@ -209,8 +207,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
val context = Context(iterator.next()) val context = Context(iterator.next())
runWriteAction { context.buildAndRunTemplate { buildNext(iterator) } } runWriteAction { context.buildAndRunTemplate { buildNext(iterator) } }
ApplicationManager.getApplication().invokeLater { context.showDialogIfNeeded() } ApplicationManager.getApplication().invokeLater { context.showDialogIfNeeded() }
} } else {
else {
runWriteAction { ShortenReferences.DEFAULT.process(elementsToShorten) } runWriteAction { ShortenReferences.DEFAULT.process(elementsToShorten) }
} }
} }
@@ -220,8 +217,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
assert(config.currentEditor != null) { "Can't run build() without editor" } assert(config.currentEditor != null) { "Can't run build() without editor" }
if (finished) throw IllegalStateException("Current builder has already finished") if (finished) throw IllegalStateException("Current builder has already finished")
buildNext(config.callableInfos.iterator()) buildNext(config.callableInfos.iterator())
} } finally {
finally {
finished = true finished = true
onFinish() onFinish()
} }
@@ -248,7 +244,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
when (placement) { when (placement) {
is CallablePlacement.NoReceiver -> { is CallablePlacement.NoReceiver -> {
containingElement = placement.containingElement containingElement = placement.containingElement
receiverClassDescriptor = with (placement.containingElement) { receiverClassDescriptor = with(placement.containingElement) {
when (this) { when (this) {
is KtClassOrObject -> currentFileContext[BindingContext.CLASS, this] is KtClassOrObject -> currentFileContext[BindingContext.CLASS, this]
is PsiClass -> getJavaClassDescriptor() is PsiClass -> getJavaClassDescriptor()
@@ -279,13 +275,12 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
jetFileToEdit = containingElement.containingKtFile jetFileToEdit = containingElement.containingKtFile
containingFileEditor = if (jetFileToEdit != config.currentFile) { containingFileEditor = if (jetFileToEdit != config.currentFile) {
FileEditorManager.getInstance(project).selectedTextEditor!! FileEditorManager.getInstance(project).selectedTextEditor!!
} } else {
else {
config.currentEditor!! config.currentEditor!!
} }
null null
} else { } else {
val dialog = object: DialogWithEditor(project, "Create from usage", "") { val dialog = object : DialogWithEditor(project, "Create from usage", "") {
override fun doOKAction() { override fun doOKAction() {
project.executeWriteCommand("Premature end of template") { project.executeWriteCommand("Premature end of template") {
TemplateManagerImpl.getTemplateState(editor)?.gotoEnd(false) TemplateManagerImpl.getTemplateState(editor)?.gotoEnd(false)
@@ -313,17 +308,18 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
if (config.enableSubstitutions) { if (config.enableSubstitutions) {
collectSubstitutionsForReceiverTypeParameters(receiverType, substitutionMap) collectSubstitutionsForReceiverTypeParameters(receiverType, substitutionMap)
val typeArgumentsForFakeFunction = callableInfo.typeParameterInfos val typeArgumentsForFakeFunction = callableInfo.typeParameterInfos
.map { .map {
val typeCandidates = computeTypeCandidates(it) val typeCandidates = computeTypeCandidates(it)
assert (typeCandidates.size == 1) { "Ambiguous type candidates for type parameter $it: $typeCandidates" } assert(typeCandidates.size == 1) { "Ambiguous type candidates for type parameter $it: $typeCandidates" }
typeCandidates.first().theType typeCandidates.first().theType
} }
.subtract(substitutionMap.keys) .subtract(substitutionMap.keys)
fakeFunction = createFakeFunctionDescriptor(scope, typeArgumentsForFakeFunction.size) fakeFunction = createFakeFunctionDescriptor(scope, typeArgumentsForFakeFunction.size)
collectSubstitutionsForCallableTypeParameters(fakeFunction, typeArgumentsForFakeFunction, substitutionMap) collectSubstitutionsForCallableTypeParameters(fakeFunction, typeArgumentsForFakeFunction, substitutionMap)
mandatoryTypeParametersAsCandidates = listOfNotNull(receiverTypeCandidate) + typeArgumentsForFakeFunction.map { TypeCandidate(substitutionMap[it]!!, scope) } mandatoryTypeParametersAsCandidates = listOfNotNull(receiverTypeCandidate) + typeArgumentsForFakeFunction.map {
} TypeCandidate(substitutionMap[it]!!, scope)
else { }
} else {
fakeFunction = null fakeFunction = null
mandatoryTypeParametersAsCandidates = Collections.emptyList() mandatoryTypeParametersAsCandidates = Collections.emptyList()
} }
@@ -362,35 +358,38 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
return receiverClassDescriptor.scopeForMemberDeclarationResolution return receiverClassDescriptor.scopeForMemberDeclarationResolution
} }
assert (receiverClassDescriptor is JavaClassDescriptor) { "Unexpected receiver class: $receiverClassDescriptor" } assert(receiverClassDescriptor is JavaClassDescriptor) { "Unexpected receiver class: $receiverClassDescriptor" }
val projections = ((receiverClassDescriptor as JavaClassDescriptor).declaredTypeParameters) val projections = ((receiverClassDescriptor as JavaClassDescriptor).declaredTypeParameters)
.map { TypeProjectionImpl(it.defaultType) } .map { TypeProjectionImpl(it.defaultType) }
val memberScope = receiverClassDescriptor.getMemberScope(projections) val memberScope = receiverClassDescriptor.getMemberScope(projections)
return LexicalScopeImpl(memberScope.memberScopeAsImportingScope(), receiverClassDescriptor, false, null, return LexicalScopeImpl(
LexicalScopeKind.SYNTHETIC) { memberScope.memberScopeAsImportingScope(), receiverClassDescriptor, false, null,
LexicalScopeKind.SYNTHETIC
) {
receiverClassDescriptor.typeConstructor.parameters.forEach { addClassifierDescriptor(it) } receiverClassDescriptor.typeConstructor.parameters.forEach { addClassifierDescriptor(it) }
} }
} }
private fun collectSubstitutionsForReceiverTypeParameters( private fun collectSubstitutionsForReceiverTypeParameters(
receiverType: KotlinType?, receiverType: KotlinType?,
result: MutableMap<KotlinType, KotlinType> result: MutableMap<KotlinType, KotlinType>
) { ) {
if (placement is CallablePlacement.NoReceiver) return if (placement is CallablePlacement.NoReceiver) return
val classTypeParameters = receiverType?.arguments ?: Collections.emptyList() val classTypeParameters = receiverType?.arguments ?: Collections.emptyList()
val ownerTypeArguments = (placement as? CallablePlacement.WithReceiver)?.receiverTypeCandidate?.theType?.arguments val ownerTypeArguments = (placement as? CallablePlacement.WithReceiver)?.receiverTypeCandidate?.theType?.arguments
?: Collections.emptyList() ?: Collections.emptyList()
assert(ownerTypeArguments.size == classTypeParameters.size) assert(ownerTypeArguments.size == classTypeParameters.size)
ownerTypeArguments.zip(classTypeParameters).forEach { result[it.first.type] = it.second.type } ownerTypeArguments.zip(classTypeParameters).forEach { result[it.first.type] = it.second.type }
} }
private fun collectSubstitutionsForCallableTypeParameters( private fun collectSubstitutionsForCallableTypeParameters(
fakeFunction: FunctionDescriptor, fakeFunction: FunctionDescriptor,
typeArguments: Set<KotlinType>, typeArguments: Set<KotlinType>,
result: MutableMap<KotlinType, KotlinType>) { result: MutableMap<KotlinType, KotlinType>
) {
for ((typeArgument, typeParameter) in typeArguments.zip(fakeFunction.typeParameters)) { for ((typeArgument, typeParameter) in typeArguments.zip(fakeFunction.typeParameters)) {
result[typeArgument] = typeParameter.defaultType result[typeArgument] = typeParameter.defaultType
} }
@@ -398,34 +397,36 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
private fun createFakeFunctionDescriptor(scope: HierarchicalScope, typeParameterCount: Int): FunctionDescriptor { private fun createFakeFunctionDescriptor(scope: HierarchicalScope, typeParameterCount: Int): FunctionDescriptor {
val fakeFunction = SimpleFunctionDescriptorImpl.create( val fakeFunction = SimpleFunctionDescriptorImpl.create(
MutablePackageFragmentDescriptor(currentFileModule, FqName("fake")), MutablePackageFragmentDescriptor(currentFileModule, FqName("fake")),
Annotations.EMPTY, Annotations.EMPTY,
Name.identifier("fake"), Name.identifier("fake"),
CallableMemberDescriptor.Kind.SYNTHESIZED, CallableMemberDescriptor.Kind.SYNTHESIZED,
SourceElement.NO_SOURCE SourceElement.NO_SOURCE
) )
val validator = CollectingNameValidator { scope.findClassifier(Name.identifier(it), NoLookupLocation.FROM_IDE) == null } val validator = CollectingNameValidator { scope.findClassifier(Name.identifier(it), NoLookupLocation.FROM_IDE) == null }
val parameterNames = KotlinNameSuggester.suggestNamesForTypeParameters(typeParameterCount, validator) val parameterNames = KotlinNameSuggester.suggestNamesForTypeParameters(typeParameterCount, validator)
val typeParameters = (0..typeParameterCount - 1).map { val typeParameters = (0 until typeParameterCount).map {
TypeParameterDescriptorImpl.createWithDefaultBound( TypeParameterDescriptorImpl.createWithDefaultBound(
fakeFunction, fakeFunction,
Annotations.EMPTY, Annotations.EMPTY,
false, false,
Variance.INVARIANT, Variance.INVARIANT,
Name.identifier(parameterNames[it]), Name.identifier(parameterNames[it]),
it it
) )
} }
return fakeFunction.initialize(null, null, typeParameters, Collections.emptyList(), null, return fakeFunction.initialize(
null, Visibilities.INTERNAL) null, null, typeParameters, Collections.emptyList(), null,
null, Visibilities.INTERNAL
)
} }
private fun renderTypeCandidates( private fun renderTypeCandidates(
typeInfo: TypeInfo, typeInfo: TypeInfo,
typeParameterNameMap: Map<TypeParameterDescriptor, String>, typeParameterNameMap: Map<TypeParameterDescriptor, String>,
fakeFunction: FunctionDescriptor? fakeFunction: FunctionDescriptor?
) { ) {
typeCandidates[typeInfo]?.forEach { it.render(typeParameterNameMap, fakeFunction) } typeCandidates[typeInfo]?.forEach { it.render(typeParameterNameMap, fakeFunction) }
} }
@@ -436,12 +437,11 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
} }
private fun createDeclarationSkeleton(): KtNamedDeclaration { private fun createDeclarationSkeleton(): KtNamedDeclaration {
with (config) { with(config) {
val assignmentToReplace = val assignmentToReplace =
if (containingElement is KtBlockExpression && (callableInfo as? PropertyInfo)?.writable ?: false) { if (containingElement is KtBlockExpression && (callableInfo as? PropertyInfo)?.writable == true) {
originalElement as KtBinaryExpression originalElement as KtBinaryExpression
} } else null
else null
val ownerTypeString = if (isExtension) { val ownerTypeString = if (isExtension) {
val renderedType = receiverTypeCandidate!!.renderedTypes.first() val renderedType = receiverTypeCandidate!!.renderedTypes.first()
@@ -455,8 +455,9 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
val prefix = if (classKind == ClassKind.ANNOTATION_CLASS) "val " else "" val prefix = if (classKind == ClassKind.ANNOTATION_CLASS) "val " else ""
val list = callableInfo.parameterInfos.indices.joinToString(", ") { i -> "${prefix}p$i: Any" } val list = callableInfo.parameterInfos.indices.joinToString(", ") { i -> "${prefix}p$i: Any" }
return if (callableInfo.parameterInfos.isNotEmpty() return if (callableInfo.parameterInfos.isNotEmpty()
|| callableInfo.kind == CallableKind.FUNCTION || callableInfo.kind == CallableKind.FUNCTION
|| callableInfo.kind == CallableKind.CONSTRUCTOR) "($list)" else list || callableInfo.kind == CallableKind.CONSTRUCTOR
) "($list)" else list
} }
val paramList = when (callableInfo.kind) { val paramList = when (callableInfo.kind) {
@@ -474,13 +475,14 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
val visibilityKeyword = modifierList.visibilityModifierType() val visibilityKeyword = modifierList.visibilityModifierType()
if (visibilityKeyword == null) { if (visibilityKeyword == null) {
val defaultVisibility = val defaultVisibility =
if (callableInfo.isAbstract) "" if (callableInfo.isAbstract) ""
else if (containingElement is KtClassOrObject else if (containingElement is KtClassOrObject
&& !(containingElement is KtClass && containingElement.isInterface()) && !(containingElement is KtClass && containingElement.isInterface())
&& containingElement.isAncestor(config.originalElement) && containingElement.isAncestor(config.originalElement)
&& callableInfo.kind != CallableKind.CONSTRUCTOR) "private " && callableInfo.kind != CallableKind.CONSTRUCTOR
else if (isExtension) "private " ) "private "
else "" else if (isExtension) "private "
else ""
append(defaultVisibility) append(defaultVisibility)
} }
@@ -488,7 +490,8 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
if (callableInfo.isAbstract if (callableInfo.isAbstract
&& containingElement is KtClass && containingElement is KtClass
&& !containingElement.isInterface() && !containingElement.isInterface()
&& !modifierList.hasModifier(KtTokens.ABSTRACT_KEYWORD)) { && !modifierList.hasModifier(KtTokens.ABSTRACT_KEYWORD)
) {
modifierList.appendModifier(KtTokens.ABSTRACT_KEYWORD) modifierList.appendModifier(KtTokens.ABSTRACT_KEYWORD)
} }
@@ -510,22 +513,20 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
containingElement is KtClass && containingElement.hasModifier(KtTokens.EXTERNAL_KEYWORD) -> "" containingElement is KtClass && containingElement.hasModifier(KtTokens.EXTERNAL_KEYWORD) -> ""
containingElement is KtObjectDeclaration && containingElement.hasModifier(KtTokens.EXTERNAL_KEYWORD) -> "" containingElement is KtObjectDeclaration && containingElement.hasModifier(KtTokens.EXTERNAL_KEYWORD) -> ""
containingElement is KtObjectDeclaration && containingElement.isCompanion() containingElement is KtObjectDeclaration && containingElement.isCompanion()
&& containingElement.parent.parent is KtClass && containingElement.parent.parent is KtClass
&& (containingElement.parent.parent as KtClass).hasModifier(KtTokens.EXTERNAL_KEYWORD) -> "" && (containingElement.parent.parent as KtClass).hasModifier(KtTokens.EXTERNAL_KEYWORD) -> ""
isExpectClassMember -> "" isExpectClassMember -> ""
else -> "{\n\n}" else -> "{\n\n}"
} }
@Suppress("USELESS_CAST") // KT-10755 @Suppress("USELESS_CAST") // KT-10755
if (callableInfo is FunctionInfo) { when {
psiFactory.createFunction("${modifiers}fun<> $header $body") as KtNamedDeclaration callableInfo is FunctionInfo -> psiFactory.createFunction("${modifiers}fun<> $header $body") as KtNamedDeclaration
} (callableInfo as ConstructorInfo).isPrimary -> {
else if ((callableInfo as ConstructorInfo).isPrimary) { val constructorText = if (modifiers.isNotEmpty()) "${modifiers}constructor$paramList" else paramList
val constructorText = if (modifiers.isNotEmpty()) "${modifiers}constructor$paramList" else paramList psiFactory.createPrimaryConstructor(constructorText) as KtNamedDeclaration
psiFactory.createPrimaryConstructor(constructorText) as KtNamedDeclaration }
} else -> psiFactory.createSecondaryConstructor("${modifiers}constructor$paramList $body") as KtNamedDeclaration
else {
psiFactory.createSecondaryConstructor("${modifiers}constructor$paramList $body") as KtNamedDeclaration
} }
} }
CallableKind.CLASS_WITH_PRIMARY_CONSTRUCTOR -> { CallableKind.CLASS_WITH_PRIMARY_CONSTRUCTOR -> {
@@ -550,7 +551,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
else -> "" else -> ""
} }
psiFactory.createDeclaration<KtClassOrObject>( psiFactory.createDeclaration<KtClassOrObject>(
"$openMod$innerMod${kind.keyword} $safeName$typeParamList$paramList$returnTypeString $classBody" "$openMod$innerMod${kind.keyword} $safeName$typeParamList$paramList$returnTypeString $classBody"
) )
} }
} }
@@ -566,8 +567,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
append("\nset() {}") append("\nset() {}")
} }
} }
} } else ""
else ""
psiFactory.createProperty("$modifiers$valVar<> $header$accessors") psiFactory.createProperty("$modifiers$valVar<> $header$accessors")
} }
} }
@@ -579,16 +579,14 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
val container = if (containingElement is KtClass && callableInfo.isForCompanion) { val container = if (containingElement is KtClass && callableInfo.isForCompanion) {
containingElement.getOrCreateCompanionObject() containingElement.getOrCreateCompanionObject()
} } else containingElement
else containingElement
val declarationInPlace = placeDeclarationInContainer(declaration, container, config.originalElement, jetFileToEdit) val declarationInPlace = placeDeclarationInContainer(declaration, container, config.originalElement, jetFileToEdit)
if (declarationInPlace is KtSecondaryConstructor) { if (declarationInPlace is KtSecondaryConstructor) {
val containingClass = declarationInPlace.containingClassOrObject!! val containingClass = declarationInPlace.containingClassOrObject!!
if (containingClass.primaryConstructorParameters.isNotEmpty()) { if (containingClass.primaryConstructorParameters.isNotEmpty()) {
declarationInPlace.replaceImplicitDelegationCallWithExplicit(true) declarationInPlace.replaceImplicitDelegationCallWithExplicit(true)
} } else if ((receiverClassDescriptor as ClassDescriptor).getSuperClassOrAny().constructors.all { it.valueParameters.isNotEmpty() }) {
else if ((receiverClassDescriptor as ClassDescriptor).getSuperClassOrAny().constructors.all { it.valueParameters.isNotEmpty() }) {
declarationInPlace.replaceImplicitDelegationCallWithExplicit(false) declarationInPlace.replaceImplicitDelegationCallWithExplicit(false)
} }
} }
@@ -601,9 +599,9 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
val allTypeParametersNotInScope = LinkedHashSet<TypeParameterDescriptor>() val allTypeParametersNotInScope = LinkedHashSet<TypeParameterDescriptor>()
mandatoryTypeParametersAsCandidates.asSequence() mandatoryTypeParametersAsCandidates.asSequence()
.plus(callableInfo.parameterInfos.asSequence().flatMap { typeCandidates[it.typeInfo]!!.asSequence() }) .plus(callableInfo.parameterInfos.asSequence().flatMap { typeCandidates[it.typeInfo]!!.asSequence() })
.flatMap { it.typeParameters.asSequence() } .flatMap { it.typeParameters.asSequence() }
.toCollection(allTypeParametersNotInScope) .toCollection(allTypeParametersNotInScope)
if (!skipReturnType) { if (!skipReturnType) {
computeTypeCandidates(callableInfo.returnTypeInfo).asSequence().flatMapTo(allTypeParametersNotInScope) { computeTypeCandidates(callableInfo.returnTypeInfo).asSequence().flatMapTo(allTypeParametersNotInScope) {
@@ -612,13 +610,17 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
} }
val validator = CollectingNameValidator { scope.findClassifier(Name.identifier(it), NoLookupLocation.FROM_IDE) == null } val validator = CollectingNameValidator { scope.findClassifier(Name.identifier(it), NoLookupLocation.FROM_IDE) == null }
val typeParameterNames = allTypeParametersNotInScope.map { KotlinNameSuggester.suggestNameByName(it.name.asString(), validator) } val typeParameterNames = allTypeParametersNotInScope.map {
KotlinNameSuggester.suggestNameByName(it.name.asString(), validator)
}
return allTypeParametersNotInScope.zip(typeParameterNames).toMap() return allTypeParametersNotInScope.zip(typeParameterNames).toMap()
} }
private fun setupTypeReferencesForShortening(declaration: KtNamedDeclaration, private fun setupTypeReferencesForShortening(
parameterTypeExpressions: List<TypeExpression>) { declaration: KtNamedDeclaration,
parameterTypeExpressions: List<TypeExpression>
) {
if (config.isExtension) { if (config.isExtension) {
val receiverTypeText = receiverTypeCandidate!!.theType.renderLong(typeParameterNameMap).first() val receiverTypeText = receiverTypeCandidate!!.theType.renderLong(typeParameterNameMap).first()
val replacingTypeRef = KtPsiFactory(declaration).createType(receiverTypeText) val replacingTypeRef = KtPsiFactory(declaration).createType(receiverTypeText)
@@ -628,7 +630,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
val returnTypeRefs = declaration.getReturnTypeReferences() val returnTypeRefs = declaration.getReturnTypeReferences()
if (returnTypeRefs.isNotEmpty()) { if (returnTypeRefs.isNotEmpty()) {
val returnType = typeCandidates[callableInfo.returnTypeInfo]!!.getTypeByRenderedType( val returnType = typeCandidates[callableInfo.returnTypeInfo]!!.getTypeByRenderedType(
returnTypeRefs.map { it.text } returnTypeRefs.map { it.text }
) )
if (returnType != null) { if (returnType != null) {
// user selected a given type // user selected a given type
@@ -643,7 +645,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
val parameterTypeRef = parameter.typeReference val parameterTypeRef = parameter.typeReference
if (parameterTypeRef != null) { if (parameterTypeRef != null) {
val parameterType = parameterTypeExpressions[i].typeCandidates.getTypeByRenderedType( val parameterType = parameterTypeExpressions[i].typeCandidates.getTypeByRenderedType(
listOf(parameterTypeRef.text) listOf(parameterTypeRef.text)
) )
if (parameterType != null) { if (parameterType != null) {
replaceWithLongerName(listOf(parameterTypeRef), parameterType) replaceWithLongerName(listOf(parameterTypeRef), parameterType)
@@ -675,11 +677,11 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
if (skipReturnType && callableInfo is FunctionInfo && callableInfo.preferEmptyBody) return if (skipReturnType && callableInfo is FunctionInfo && callableInfo.preferEmptyBody) return
val oldBody = func.bodyExpression ?: return val oldBody = func.bodyExpression ?: return
val bodyText = getFunctionBodyTextFromTemplate( val bodyText = getFunctionBodyTextFromTemplate(
func.project, func.project,
TemplateKind.FUNCTION, TemplateKind.FUNCTION,
if (callableInfo.name.isNotEmpty()) callableInfo.name else null, if (callableInfo.name.isNotEmpty()) callableInfo.name else null,
if (skipReturnType) "Unit" else (func as? KtFunction)?.typeReference?.text ?: "", if (skipReturnType) "Unit" else (func as? KtFunction)?.typeReference?.text ?: "",
receiverClassDescriptor?.importableFqName ?: receiverClassDescriptor?.name?.let { FqName.topLevel(it) } receiverClassDescriptor?.importableFqName ?: receiverClassDescriptor?.name?.let { FqName.topLevel(it) }
) )
oldBody.replace(KtPsiFactory(func).createBlock(bodyText)) oldBody.replace(KtPsiFactory(func).createBlock(bodyText))
} }
@@ -692,8 +694,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
} }
if (renderedTypeArgs.isEmpty()) { if (renderedTypeArgs.isEmpty()) {
oldTypeArgumentList.delete() oldTypeArgumentList.delete()
} } else {
else {
oldTypeArgumentList.replace(KtPsiFactory(callElement).createTypeArguments(renderedTypeArgs.joinToString(", ", "<", ">"))) oldTypeArgumentList.replace(KtPsiFactory(callElement).createTypeArguments(renderedTypeArgs.joinToString(", ", "<", ">")))
elementsToShorten.add(callElement.typeArgumentList!!) elementsToShorten.add(callElement.typeArgumentList!!)
} }
@@ -733,8 +734,8 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
} }
private fun setupTypeParameterListTemplate( private fun setupTypeParameterListTemplate(
builder: TemplateBuilderImpl, builder: TemplateBuilderImpl,
declaration: KtNamedDeclaration declaration: KtNamedDeclaration
): TypeParameterListExpression? { ): TypeParameterListExpression? {
when (declaration) { when (declaration) {
is KtObjectDeclaration -> return null is KtObjectDeclaration -> return null
@@ -750,8 +751,8 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
mandatoryTypeParametersAsCandidates.asSequence().flatMapTo(mandatoryTypeParameters) { it.renderedTypeParameters!!.asSequence() } mandatoryTypeParametersAsCandidates.asSequence().flatMapTo(mandatoryTypeParameters) { it.renderedTypeParameters!!.asSequence() }
callableInfo.parameterInfos.asSequence() callableInfo.parameterInfos.asSequence()
.flatMap { typeCandidates[it.typeInfo]!!.asSequence() } .flatMap { typeCandidates[it.typeInfo]!!.asSequence() }
.forEach { typeParameterMap[it.renderedTypes.first()] = it.renderedTypeParameters!! } .forEach { typeParameterMap[it.renderedTypes.first()] = it.renderedTypeParameters!! }
if (declaration.getReturnTypeReference() != null) { if (declaration.getReturnTypeReference() != null) {
typeCandidates[callableInfo.returnTypeInfo]!!.forEach { typeCandidates[callableInfo.returnTypeInfo]!!.forEach {
@@ -760,12 +761,12 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
} }
val expression = TypeParameterListExpression( val expression = TypeParameterListExpression(
mandatoryTypeParameters, mandatoryTypeParameters,
typeParameterMap, typeParameterMap,
callableInfo.kind != CallableKind.CLASS_WITH_PRIMARY_CONSTRUCTOR callableInfo.kind != CallableKind.CLASS_WITH_PRIMARY_CONSTRUCTOR
) )
val leftSpace = typeParameterList.prevSibling as? PsiWhiteSpace val leftSpace = typeParameterList.prevSibling as? PsiWhiteSpace
val rangeStart = if (leftSpace != null) leftSpace.startOffset else typeParameterList.startOffset val rangeStart = leftSpace?.startOffset ?: typeParameterList.startOffset
val offset = typeParameterList.startOffset val offset = typeParameterList.startOffset
val range = UnfairTextRange(rangeStart - offset, typeParameterList.endOffset - offset) val range = UnfairTextRange(rangeStart - offset, typeParameterList.endOffset - offset)
builder.replaceElement(typeParameterList, range, "TYPE_PARAMETER_LIST", expression, false) builder.replaceElement(typeParameterList, range, "TYPE_PARAMETER_LIST", expression, false)
@@ -819,8 +820,8 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
val adjustedDeclaration = when (declaration) { val adjustedDeclaration = when (declaration) {
is KtNamedFunction, is KtProperty -> { is KtNamedFunction, is KtProperty -> {
val klass = psiFactory.createClass("class Foo {}") val klass = psiFactory.createClass("class Foo {}")
klass.getBody()!!.add(declaration) klass.body!!.add(declaration)
(declaration.replace(klass) as KtClass).getBody()!!.declarations.first() (declaration.replace(klass) as KtClass).body!!.declarations.first()
} }
else -> declaration else -> declaration
} }
@@ -876,8 +877,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
if (superCall != null) { if (superCall != null) {
val lParen = superCall.argumentList.firstChild val lParen = superCall.argumentList.firstChild
targetEditor.caretModel.moveToOffset(lParen.endOffset) targetEditor.caretModel.moveToOffset(lParen.endOffset)
} } else {
else {
targetEditor.caretModel.moveToOffset(newJavaMember.startOffset) targetEditor.caretModel.moveToOffset(newJavaMember.startOffset)
} }
} }
@@ -962,10 +962,12 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
if (brokenOff && !ApplicationManager.getApplication().isUnitTestMode) return if (brokenOff && !ApplicationManager.getApplication().isUnitTestMode) return
// file templates // file templates
val newDeclaration = PsiTreeUtil.findElementOfClassAtOffset(jetFileToEdit, val newDeclaration = PsiTreeUtil.findElementOfClassAtOffset(
declarationMarker.startOffset, jetFileToEdit,
declaration::class.java, declarationMarker.startOffset,
false) ?: return declaration::class.java,
false
) ?: return
runWriteAction { runWriteAction {
postprocessDeclaration(newDeclaration) postprocessDeclaration(newDeclaration)
@@ -982,7 +984,8 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
&& newDeclaration.initializer == null && newDeclaration.initializer == null
&& callableInfo is PropertyInfo && callableInfo is PropertyInfo
&& callableInfo.withInitializer && callableInfo.withInitializer
&& !callableInfo.isLateinitPreferred) { && !callableInfo.isLateinitPreferred
) {
newDeclaration.initializer = KtPsiFactory(newDeclaration).createExpression("TODO(\"initialize me\")") newDeclaration.initializer = KtPsiFactory(newDeclaration).createExpression("TODO(\"initialize me\")")
} }
} }
@@ -1001,8 +1004,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
setupEditor(newDeclaration) setupEditor(newDeclaration)
} }
} }
} } finally {
finally {
declarationMarker.dispose() declarationMarker.dispose()
finished = true finished = true
onFinish() onFinish()
@@ -1030,10 +1032,10 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
// TODO: Simplify and use formatter as much as possible // TODO: Simplify and use formatter as much as possible
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
internal fun <D : KtNamedDeclaration> placeDeclarationInContainer( internal fun <D : KtNamedDeclaration> placeDeclarationInContainer(
declaration: D, declaration: D,
container: PsiElement, container: PsiElement,
anchor: PsiElement, anchor: PsiElement,
fileToEdit: KtFile = container.containingFile as KtFile fileToEdit: KtFile = container.containingFile as KtFile
): D { ): D {
val psiFactory = KtPsiFactory(container) val psiFactory = KtPsiFactory(container)
val newLine = psiFactory.createNewLine() val newLine = psiFactory.createNewLine()
@@ -1065,17 +1067,18 @@ internal fun <D : KtNamedDeclaration> placeDeclarationInContainer(
val actualContainer = (container as? KtClassOrObject)?.getOrCreateBody() ?: container val actualContainer = (container as? KtClassOrObject)?.getOrCreateBody() ?: container
fun addDeclarationToClassOrObject(classOrObject: KtClassOrObject, fun addDeclarationToClassOrObject(
declaration: KtNamedDeclaration): KtNamedDeclaration { classOrObject: KtClassOrObject,
declaration: KtNamedDeclaration
): KtNamedDeclaration {
val classBody = classOrObject.getOrCreateBody() val classBody = classOrObject.getOrCreateBody()
return if (declaration is KtNamedFunction) { return if (declaration is KtNamedFunction) {
val neighbor = PsiTreeUtil.skipSiblingsBackward( val neighbor = PsiTreeUtil.skipSiblingsBackward(
classBody.rBrace ?: classBody.lastChild!!, classBody.rBrace ?: classBody.lastChild!!,
PsiWhiteSpace::class.java PsiWhiteSpace::class.java
) )
classBody.addAfter(declaration, neighbor) as KtNamedDeclaration classBody.addAfter(declaration, neighbor) as KtNamedDeclaration
} } else classBody.addAfter(declaration, classBody.lBrace!!) as KtNamedDeclaration
else classBody.addAfter(declaration, classBody.lBrace!!) as KtNamedDeclaration
} }
@@ -1083,8 +1086,7 @@ internal fun <D : KtNamedDeclaration> placeDeclarationInContainer(
val sibling = anchor.parentsWithSelf.first { it.parent == actualContainer } val sibling = anchor.parentsWithSelf.first { it.parent == actualContainer }
return if (addBefore) { return if (addBefore) {
actualContainer.addBefore(declaration, sibling) actualContainer.addBefore(declaration, sibling)
} } else {
else {
actualContainer.addAfter(declaration, sibling) actualContainer.addAfter(declaration, sibling)
} as D } as D
} }
@@ -1105,9 +1107,11 @@ internal fun <D : KtNamedDeclaration> placeDeclarationInContainer(
} }
} }
} }
addNextToOriginalElementContainer(insertToBlock addNextToOriginalElementContainer(
|| (declaration is KtProperty && actualContainer !is KtFile) insertToBlock
|| declaration is KtTypeAlias) || (declaration is KtProperty && actualContainer !is KtFile)
|| declaration is KtTypeAlias
)
} }
container is KtFile -> container.add(declaration) as D container is KtFile -> container.add(declaration) as D
@@ -1117,8 +1121,7 @@ internal fun <D : KtNamedDeclaration> placeDeclarationInContainer(
val wrappingClass = psiFactory.createClass("class ${container.name} {\n}") val wrappingClass = psiFactory.createClass("class ${container.name} {\n}")
addDeclarationToClassOrObject(wrappingClass, declaration) addDeclarationToClassOrObject(wrappingClass, declaration)
(fileToEdit.add(wrappingClass) as KtClass).declarations.first() as D (fileToEdit.add(wrappingClass) as KtClass).declarations.first() as D
} } else {
else {
fileToEdit.add(declaration) as D fileToEdit.add(declaration) as D
} }
} }
@@ -1126,7 +1129,7 @@ internal fun <D : KtNamedDeclaration> placeDeclarationInContainer(
container is KtClassOrObject -> { container is KtClassOrObject -> {
var sibling: PsiElement? = container.declarations.lastOrNull { it::class == declaration::class } var sibling: PsiElement? = container.declarations.lastOrNull { it::class == declaration::class }
if (sibling == null && declaration is KtProperty) { if (sibling == null && declaration is KtProperty) {
sibling = container.getBody()?.lBrace sibling = container.body?.lBrace
} }
insertMember(null, container, declaration, sibling) insertMember(null, container, declaration, sibling)