From a6c4d2964051bfd618e74532ef69f881ef5418aa Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Thu, 15 Oct 2015 23:15:58 +0300 Subject: [PATCH] More correct --- .../quickfix/AddFunctionToSupertypeFix.kt | 108 ++++++++++-------- 1 file changed, 61 insertions(+), 47 deletions(-) diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddFunctionToSupertypeFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddFunctionToSupertypeFix.kt index d3448a4e8bc..1fd5126c390 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddFunctionToSupertypeFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddFunctionToSupertypeFix.kt @@ -24,15 +24,12 @@ import com.intellij.openapi.ui.popup.JBPopupFactory import com.intellij.openapi.ui.popup.ListPopupStep import com.intellij.openapi.ui.popup.PopupStep import com.intellij.openapi.ui.popup.util.BaseListPopupStep -import com.intellij.psi.PsiDocumentManager -import com.intellij.psi.PsiFile import com.intellij.util.PlatformIcons import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde -import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.ShortenReferences import org.jetbrains.kotlin.idea.util.application.executeWriteCommand @@ -40,21 +37,28 @@ import org.jetbrains.kotlin.psi.JetClass import org.jetbrains.kotlin.psi.JetFile import org.jetbrains.kotlin.psi.JetNamedFunction import org.jetbrains.kotlin.psi.JetPsiFactory -import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.checker.JetTypeChecker import org.jetbrains.kotlin.types.typeUtil.supertypes import java.util.* -class AddFunctionToSupertypeFix(element: JetNamedFunction) : KotlinQuickFixAction(element) { - private val functionsToAdd = generateFunctionsToAdd(element) +class AddFunctionToSupertypeFix private constructor( + element: JetNamedFunction, + private val functions: List +) : KotlinQuickFixAction(element) { - override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean { - return super.isAvailable(project, editor, file) && !functionsToAdd.isEmpty() + init { + assert(functions.isNotEmpty()) } + private class FunctionData( + val signaturePreview: String, + val sourceCode: String, + val targetClass: JetClass + ) + override fun getText(): String { - val single = functionsToAdd.singleOrNull() + val single = functions.singleOrNull() return if (single != null) actionName(single) else @@ -66,8 +70,8 @@ class AddFunctionToSupertypeFix(element: JetNamedFunction) : KotlinQuickFixActio override fun invoke(project: Project, editor: Editor?, file: JetFile) { CommandProcessor.getInstance().runUndoTransparentAction(object : Runnable { override fun run() { - if (functionsToAdd.size == 1 || editor == null || !editor.component.isShowing) { - addFunction(functionsToAdd.first(), project) + if (functions.size == 1 || editor == null || !editor.component.isShowing) { + addFunction(functions.first(), project) } else { JBPopupFactory.getInstance().createListPopup(createFunctionPopup(project)).showInBestPositionFor(editor) @@ -76,61 +80,69 @@ class AddFunctionToSupertypeFix(element: JetNamedFunction) : KotlinQuickFixActio }) } - private fun addFunction(functionDescriptor: FunctionDescriptor, project: Project) { - val typeDescriptor = functionDescriptor.containingDeclaration as ClassDescriptor - - val signatureString = IdeDescriptorRenderers.SOURCE_CODE.render(functionDescriptor) - - PsiDocumentManager.getInstance(project).commitAllDocuments() - - val classifierDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, typeDescriptor) as JetClass - + private fun addFunction(functionData: FunctionData, project: Project) { project.executeWriteCommand("Add Function to Type") { - val body = classifierDeclaration.getOrCreateBody() + val classBody = functionData.targetClass.getOrCreateBody() - var functionBody = "" - if (typeDescriptor.kind != ClassKind.INTERFACE && functionDescriptor.modality != Modality.ABSTRACT) { - functionBody = "{}" - val returnType = functionDescriptor.returnType - if (returnType == null || !KotlinBuiltIns.isUnit(returnType)) { - functionBody = "{ throw UnsupportedOperationException() }" - } - } - - val functionElement = JetPsiFactory(project).createFunction(signatureString + functionBody) - val insertedFunctionElement = body.addBefore(functionElement, body.rBrace) as JetNamedFunction + val functionElement = JetPsiFactory(project).createFunction(functionData.sourceCode) + val insertedFunctionElement = classBody.addBefore(functionElement, classBody.rBrace) as JetNamedFunction ShortenReferences.DEFAULT.process(insertedFunctionElement) } } private fun createFunctionPopup(project: Project): ListPopupStep<*> { - return object : BaseListPopupStep("Choose Type", functionsToAdd) { + return object : BaseListPopupStep("Choose Type", functions) { override fun isAutoSelectionEnabled() = false - override fun onChosen(selectedValue: FunctionDescriptor, finalChoice: Boolean): PopupStep? { + override fun onChosen(selectedValue: FunctionData, finalChoice: Boolean): PopupStep<*>? { if (finalChoice) { addFunction(selectedValue, project) } return PopupStep.FINAL_CHOICE } - override fun getIconFor(aValue: FunctionDescriptor) = PlatformIcons.FUNCTION_ICON - - override fun getTextFor(functionDescriptor: FunctionDescriptor) = actionName(functionDescriptor) + override fun getIconFor(value: FunctionData) = PlatformIcons.FUNCTION_ICON + override fun getTextFor(value: FunctionData) = actionName(value) } } - private fun actionName(functionDescriptor: FunctionDescriptor): String { - val signature = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(functionDescriptor) - val className = functionDescriptor.containingDeclaration.name.asString() - return "Add '$signature' to '$className'" - } + private fun actionName(functionData: FunctionData) + = "Add '${functionData.signaturePreview}' to '${functionData.targetClass.name}'" companion object: JetSingleIntentionActionFactory() { override fun createAction(diagnostic: Diagnostic): IntentionAction? { - val function = QuickFixUtil.getParentElementOfType(diagnostic, JetNamedFunction::class.java) - return if (function == null) null else AddFunctionToSupertypeFix(function) + val function = diagnostic.psiElement as? JetNamedFunction ?: return null + + val descriptors = generateFunctionsToAdd(function) + if (descriptors.isEmpty()) return null + + val project = diagnostic.psiFile.project + val functionData = descriptors.map { createFunctionData(it, project) }.filterNotNull() + if (functionData.isEmpty()) return null + + return AddFunctionToSupertypeFix(function, functionData) + } + + private fun createFunctionData(functionDescriptor: FunctionDescriptor, project: Project): FunctionData? { + val classDescriptor = functionDescriptor.containingDeclaration as ClassDescriptor + + var sourceCode = IdeDescriptorRenderers.SOURCE_CODE.render(functionDescriptor) + if (classDescriptor.kind != ClassKind.INTERFACE && functionDescriptor.modality != Modality.ABSTRACT) { + val returnType = functionDescriptor.returnType + if (returnType == null || !KotlinBuiltIns.isUnit(returnType)) { + sourceCode += "{ throw UnsupportedOperationException() }" + } + else { + sourceCode += "{}" + } + } + + val targetClass = DescriptorToSourceUtilsIde.getAnyDeclaration(project, classDescriptor) as? JetClass ?: return null + return FunctionData( + IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(functionDescriptor), + sourceCode, + targetClass) } private fun generateFunctionsToAdd(functionElement: JetNamedFunction): List { @@ -139,12 +151,12 @@ class AddFunctionToSupertypeFix(element: JetNamedFunction) : KotlinQuickFixActio 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). - return getSupertypes(containingClass) + return getSuperClasses(containingClass) .filterNot { KotlinBuiltIns.isAnyOrNullableAny(it.defaultType) } .map { generateFunctionSignatureForType(functionDescriptor, it) } } - private fun getSupertypes(classDescriptor: ClassDescriptor): List { + private fun getSuperClasses(classDescriptor: ClassDescriptor): List { val supertypes = classDescriptor.defaultType.supertypes().sortedWith(object : Comparator { override fun compare(o1: JetType, o2: JetType): Int { return when { @@ -156,7 +168,9 @@ class AddFunctionToSupertypeFix(element: JetNamedFunction) : KotlinQuickFixActio } }) - return supertypes.map { DescriptorUtils.getClassDescriptorForType(it) } + return supertypes + .map { it.constructor.declarationDescriptor as? ClassDescriptor } + .filterNotNull() } private fun generateFunctionSignatureForType(functionDescriptor: FunctionDescriptor, typeDescriptor: ClassDescriptor): FunctionDescriptor {