Add function to supertype fix: reformat

This commit is contained in:
Mikhail Glukhikh
2018-01-18 15:33:36 +03:00
parent 925e4e0a67
commit 7f947bed1e
@@ -42,14 +42,13 @@ import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.modalityModifier import org.jetbrains.kotlin.psi.psiUtil.modalityModifier
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.typeUtil.supertypes import org.jetbrains.kotlin.types.typeUtil.supertypes
import java.util.* import java.util.*
class AddFunctionToSupertypeFix private constructor( class AddFunctionToSupertypeFix private constructor(
element: KtNamedFunction, element: KtNamedFunction,
private val functions: List<AddFunctionToSupertypeFix.FunctionData> private val functions: List<AddFunctionToSupertypeFix.FunctionData>
) : KotlinQuickFixAction<KtNamedFunction>(element), LowPriorityAction { ) : KotlinQuickFixAction<KtNamedFunction>(element), LowPriorityAction {
init { init {
@@ -57,9 +56,9 @@ class AddFunctionToSupertypeFix private constructor(
} }
private class FunctionData( private class FunctionData(
val signaturePreview: String, val signaturePreview: String,
val sourceCode: String, val sourceCode: String,
val targetClass: KtClass val targetClass: KtClass
) )
override fun getText(): String { override fun getText(): String {
@@ -76,8 +75,7 @@ class AddFunctionToSupertypeFix private constructor(
CommandProcessor.getInstance().runUndoTransparentAction { CommandProcessor.getInstance().runUndoTransparentAction {
if (functions.size == 1 || editor == null || !editor.component.isShowing) { if (functions.size == 1 || editor == null || !editor.component.isShowing) {
addFunction(functions.first(), project) addFunction(functions.first(), project)
} } else {
else {
JBPopupFactory.getInstance().createListPopup(createFunctionPopup(project)).showInBestPositionFor(editor) JBPopupFactory.getInstance().createListPopup(createFunctionPopup(project)).showInBestPositionFor(editor)
} }
} }
@@ -92,7 +90,7 @@ class AddFunctionToSupertypeFix private constructor(
ShortenReferences.DEFAULT.process(insertedFunctionElement) ShortenReferences.DEFAULT.process(insertedFunctionElement)
val modifierToken = insertedFunctionElement.modalityModifier()?.node?.elementType as? KtModifierKeywordToken val modifierToken = insertedFunctionElement.modalityModifier()?.node?.elementType as? KtModifierKeywordToken
?: return@executeWriteCommand ?: return@executeWriteCommand
if (insertedFunctionElement.implicitModality() == modifierToken) { if (insertedFunctionElement.implicitModality() == modifierToken) {
RemoveModifierFix(insertedFunctionElement, modifierToken, true).invoke() RemoveModifierFix(insertedFunctionElement, modifierToken, true).invoke()
} }
@@ -115,10 +113,9 @@ class AddFunctionToSupertypeFix private constructor(
} }
} }
private fun actionName(functionData: FunctionData) private fun actionName(functionData: FunctionData) = "Add '${functionData.signaturePreview}' to '${functionData.targetClass.name}'"
= "Add '${functionData.signaturePreview}' to '${functionData.targetClass.name}'"
companion object: KotlinSingleIntentionActionFactory() { companion object : KotlinSingleIntentionActionFactory() {
override fun createAction(diagnostic: Diagnostic): IntentionAction? { override fun createAction(diagnostic: Diagnostic): IntentionAction? {
val function = diagnostic.psiElement as? KtNamedFunction ?: return null val function = diagnostic.psiElement as? KtNamedFunction ?: return null
@@ -140,64 +137,68 @@ class AddFunctionToSupertypeFix private constructor(
val returnType = functionDescriptor.returnType val returnType = functionDescriptor.returnType
sourceCode += if (returnType == null || !KotlinBuiltIns.isUnit(returnType)) { sourceCode += if (returnType == null || !KotlinBuiltIns.isUnit(returnType)) {
val bodyText = getFunctionBodyTextFromTemplate( val bodyText = getFunctionBodyTextFromTemplate(
project, project,
TemplateKind.FUNCTION, TemplateKind.FUNCTION,
functionDescriptor.name.asString(), functionDescriptor.name.asString(),
functionDescriptor.returnType?.let { IdeDescriptorRenderers.SOURCE_CODE.renderType(it) } ?: "Unit", functionDescriptor.returnType?.let { IdeDescriptorRenderers.SOURCE_CODE.renderType(it) } ?: "Unit",
classDescriptor.importableFqName classDescriptor.importableFqName
) )
"{ $bodyText }" "{ $bodyText }"
} } else {
else {
"{}" "{}"
} }
} }
val targetClass = DescriptorToSourceUtilsIde.getAnyDeclaration(project, classDescriptor) as? KtClass ?: return null val targetClass = DescriptorToSourceUtilsIde.getAnyDeclaration(project, classDescriptor) as? KtClass ?: return null
return FunctionData( return FunctionData(
IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(functionDescriptor), IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(functionDescriptor),
sourceCode, sourceCode,
targetClass) targetClass
)
} }
private fun generateFunctionsToAdd(functionElement: KtNamedFunction): List<FunctionDescriptor> { private fun generateFunctionsToAdd(functionElement: KtNamedFunction): List<FunctionDescriptor> {
val functionDescriptor = functionElement.resolveToDescriptorIfAny(BodyResolveMode.FULL) as? FunctionDescriptor val functionDescriptor = functionElement.resolveToDescriptorIfAny(BodyResolveMode.FULL) as? FunctionDescriptor
?: return emptyList() ?: return emptyList()
val containingClass = functionDescriptor.containingDeclaration as? ClassDescriptor ?: return emptyList() val containingClass = functionDescriptor.containingDeclaration as? ClassDescriptor ?: return emptyList()
// TODO: filter out impossible supertypes (for example when argument's type isn't visible in a superclass). // TODO: filter out impossible supertypes (for example when argument's type isn't visible in a superclass).
return getSuperClasses(containingClass) return getSuperClasses(containingClass)
.filterNot { KotlinBuiltIns.isAnyOrNullableAny(it.defaultType) } .filterNot { KotlinBuiltIns.isAnyOrNullableAny(it.defaultType) }
.map { generateFunctionSignatureForType(functionDescriptor, it) } .map { generateFunctionSignatureForType(functionDescriptor, it) }
} }
private fun getSuperClasses(classDescriptor: ClassDescriptor): List<ClassDescriptor> { private fun getSuperClasses(classDescriptor: ClassDescriptor): List<ClassDescriptor> {
val supertypes = classDescriptor.defaultType.supertypes().sortedWith( val supertypes = classDescriptor.defaultType.supertypes().sortedWith(
Comparator<KotlinType> { o1, o2 -> Comparator { o1, o2 ->
when { when {
o1 == o2 -> 0 o1 == o2 -> 0
KotlinTypeChecker.DEFAULT.isSubtypeOf(o1, o2) -> -1 KotlinTypeChecker.DEFAULT.isSubtypeOf(o1, o2) -> -1
KotlinTypeChecker.DEFAULT.isSubtypeOf(o2, o1) -> 1 KotlinTypeChecker.DEFAULT.isSubtypeOf(o2, o1) -> 1
else -> o1.toString().compareTo(o2.toString()) else -> o1.toString().compareTo(o2.toString())
}
} }
}
) )
return supertypes.mapNotNull { it.constructor.declarationDescriptor as? ClassDescriptor } return supertypes.mapNotNull { it.constructor.declarationDescriptor as? ClassDescriptor }
} }
private fun generateFunctionSignatureForType(functionDescriptor: FunctionDescriptor, typeDescriptor: ClassDescriptor): FunctionDescriptor { private fun generateFunctionSignatureForType(
functionDescriptor: FunctionDescriptor,
typeDescriptor: ClassDescriptor
): FunctionDescriptor {
// TODO: support for generics. // TODO: support for generics.
val modality = if (typeDescriptor.kind == ClassKind.INTERFACE) Modality.ABSTRACT else typeDescriptor.modality val modality = if (typeDescriptor.kind == ClassKind.INTERFACE) Modality.ABSTRACT else typeDescriptor.modality
return functionDescriptor.copy( return functionDescriptor.copy(
typeDescriptor, typeDescriptor,
modality, modality,
functionDescriptor.visibility, functionDescriptor.visibility,
CallableMemberDescriptor.Kind.DECLARATION, CallableMemberDescriptor.Kind.DECLARATION,
/* copyOverrides = */ false) /* copyOverrides = */ false
)
} }
} }
} }