More correct
This commit is contained in:
@@ -24,15 +24,12 @@ import com.intellij.openapi.ui.popup.JBPopupFactory
|
|||||||
import com.intellij.openapi.ui.popup.ListPopupStep
|
import com.intellij.openapi.ui.popup.ListPopupStep
|
||||||
import com.intellij.openapi.ui.popup.PopupStep
|
import com.intellij.openapi.ui.popup.PopupStep
|
||||||
import com.intellij.openapi.ui.popup.util.BaseListPopupStep
|
import com.intellij.openapi.ui.popup.util.BaseListPopupStep
|
||||||
import com.intellij.psi.PsiDocumentManager
|
|
||||||
import com.intellij.psi.PsiFile
|
|
||||||
import com.intellij.util.PlatformIcons
|
import com.intellij.util.PlatformIcons
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
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.IdeDescriptorRenderers
|
||||||
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
||||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
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.JetFile
|
||||||
import org.jetbrains.kotlin.psi.JetNamedFunction
|
import org.jetbrains.kotlin.psi.JetNamedFunction
|
||||||
import org.jetbrains.kotlin.psi.JetPsiFactory
|
import org.jetbrains.kotlin.psi.JetPsiFactory
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
|
||||||
import org.jetbrains.kotlin.types.JetType
|
import org.jetbrains.kotlin.types.JetType
|
||||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
||||||
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class AddFunctionToSupertypeFix(element: JetNamedFunction) : KotlinQuickFixAction<JetNamedFunction>(element) {
|
class AddFunctionToSupertypeFix private constructor(
|
||||||
private val functionsToAdd = generateFunctionsToAdd(element)
|
element: JetNamedFunction,
|
||||||
|
private val functions: List<AddFunctionToSupertypeFix.FunctionData>
|
||||||
|
) : KotlinQuickFixAction<JetNamedFunction>(element) {
|
||||||
|
|
||||||
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean {
|
init {
|
||||||
return super.isAvailable(project, editor, file) && !functionsToAdd.isEmpty()
|
assert(functions.isNotEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class FunctionData(
|
||||||
|
val signaturePreview: String,
|
||||||
|
val sourceCode: String,
|
||||||
|
val targetClass: JetClass
|
||||||
|
)
|
||||||
|
|
||||||
override fun getText(): String {
|
override fun getText(): String {
|
||||||
val single = functionsToAdd.singleOrNull()
|
val single = functions.singleOrNull()
|
||||||
return if (single != null)
|
return if (single != null)
|
||||||
actionName(single)
|
actionName(single)
|
||||||
else
|
else
|
||||||
@@ -66,8 +70,8 @@ class AddFunctionToSupertypeFix(element: JetNamedFunction) : KotlinQuickFixActio
|
|||||||
override fun invoke(project: Project, editor: Editor?, file: JetFile) {
|
override fun invoke(project: Project, editor: Editor?, file: JetFile) {
|
||||||
CommandProcessor.getInstance().runUndoTransparentAction(object : Runnable {
|
CommandProcessor.getInstance().runUndoTransparentAction(object : Runnable {
|
||||||
override fun run() {
|
override fun run() {
|
||||||
if (functionsToAdd.size == 1 || editor == null || !editor.component.isShowing) {
|
if (functions.size == 1 || editor == null || !editor.component.isShowing) {
|
||||||
addFunction(functionsToAdd.first(), project)
|
addFunction(functions.first(), project)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
JBPopupFactory.getInstance().createListPopup(createFunctionPopup(project)).showInBestPositionFor(editor)
|
JBPopupFactory.getInstance().createListPopup(createFunctionPopup(project)).showInBestPositionFor(editor)
|
||||||
@@ -76,61 +80,69 @@ class AddFunctionToSupertypeFix(element: JetNamedFunction) : KotlinQuickFixActio
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addFunction(functionDescriptor: FunctionDescriptor, project: Project) {
|
private fun addFunction(functionData: FunctionData, 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
|
|
||||||
|
|
||||||
project.executeWriteCommand("Add Function to Type") {
|
project.executeWriteCommand("Add Function to Type") {
|
||||||
val body = classifierDeclaration.getOrCreateBody()
|
val classBody = functionData.targetClass.getOrCreateBody()
|
||||||
|
|
||||||
var functionBody = ""
|
val functionElement = JetPsiFactory(project).createFunction(functionData.sourceCode)
|
||||||
if (typeDescriptor.kind != ClassKind.INTERFACE && functionDescriptor.modality != Modality.ABSTRACT) {
|
val insertedFunctionElement = classBody.addBefore(functionElement, classBody.rBrace) as JetNamedFunction
|
||||||
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
|
|
||||||
|
|
||||||
ShortenReferences.DEFAULT.process(insertedFunctionElement)
|
ShortenReferences.DEFAULT.process(insertedFunctionElement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createFunctionPopup(project: Project): ListPopupStep<*> {
|
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 isAutoSelectionEnabled() = false
|
||||||
|
|
||||||
override fun onChosen(selectedValue: FunctionDescriptor, finalChoice: Boolean): PopupStep<Any>? {
|
override fun onChosen(selectedValue: FunctionData, finalChoice: Boolean): PopupStep<*>? {
|
||||||
if (finalChoice) {
|
if (finalChoice) {
|
||||||
addFunction(selectedValue, project)
|
addFunction(selectedValue, project)
|
||||||
}
|
}
|
||||||
return PopupStep.FINAL_CHOICE
|
return PopupStep.FINAL_CHOICE
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getIconFor(aValue: FunctionDescriptor) = PlatformIcons.FUNCTION_ICON
|
override fun getIconFor(value: FunctionData) = PlatformIcons.FUNCTION_ICON
|
||||||
|
override fun getTextFor(value: FunctionData) = actionName(value)
|
||||||
override fun getTextFor(functionDescriptor: FunctionDescriptor) = actionName(functionDescriptor)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun actionName(functionDescriptor: FunctionDescriptor): String {
|
private fun actionName(functionData: FunctionData)
|
||||||
val signature = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(functionDescriptor)
|
= "Add '${functionData.signaturePreview}' to '${functionData.targetClass.name}'"
|
||||||
val className = functionDescriptor.containingDeclaration.name.asString()
|
|
||||||
return "Add '$signature' to '$className'"
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object: JetSingleIntentionActionFactory() {
|
companion object: JetSingleIntentionActionFactory() {
|
||||||
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
||||||
val function = QuickFixUtil.getParentElementOfType(diagnostic, JetNamedFunction::class.java)
|
val function = diagnostic.psiElement as? JetNamedFunction ?: return null
|
||||||
return if (function == null) null else AddFunctionToSupertypeFix(function)
|
|
||||||
|
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> {
|
private fun generateFunctionsToAdd(functionElement: JetNamedFunction): List<FunctionDescriptor> {
|
||||||
@@ -139,12 +151,12 @@ class AddFunctionToSupertypeFix(element: JetNamedFunction) : KotlinQuickFixActio
|
|||||||
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 getSupertypes(containingClass)
|
return getSuperClasses(containingClass)
|
||||||
.filterNot { KotlinBuiltIns.isAnyOrNullableAny(it.defaultType) }
|
.filterNot { KotlinBuiltIns.isAnyOrNullableAny(it.defaultType) }
|
||||||
.map { generateFunctionSignatureForType(functionDescriptor, it) }
|
.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> {
|
val supertypes = classDescriptor.defaultType.supertypes().sortedWith(object : Comparator<JetType> {
|
||||||
override fun compare(o1: JetType, o2: JetType): Int {
|
override fun compare(o1: JetType, o2: JetType): Int {
|
||||||
return when {
|
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 {
|
private fun generateFunctionSignatureForType(functionDescriptor: FunctionDescriptor, typeDescriptor: ClassDescriptor): FunctionDescriptor {
|
||||||
|
|||||||
Reference in New Issue
Block a user