More correct

This commit is contained in:
Valentin Kipyatkov
2015-10-15 23:15:58 +03:00
parent 6dbfe11afc
commit a6c4d29640
@@ -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<JetNamedFunction>(element) {
private val functionsToAdd = generateFunctionsToAdd(element)
class AddFunctionToSupertypeFix private constructor(
element: JetNamedFunction,
private val functions: List<AddFunctionToSupertypeFix.FunctionData>
) : KotlinQuickFixAction<JetNamedFunction>(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<FunctionDescriptor>("Choose Type", functionsToAdd) {
return object : BaseListPopupStep<FunctionData>("Choose Type", functions) {
override fun isAutoSelectionEnabled() = false
override fun onChosen(selectedValue: FunctionDescriptor, finalChoice: Boolean): PopupStep<Any>? {
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<FunctionDescriptor> {
@@ -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<ClassDescriptor> {
private fun getSuperClasses(classDescriptor: ClassDescriptor): List<ClassDescriptor> {
val supertypes = classDescriptor.defaultType.supertypes().sortedWith(object : Comparator<JetType> {
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 {