ChangeMemberFunctionSignatureFix to not hold DeclarationDescriptor's
This commit is contained in:
@@ -27,7 +27,6 @@ import com.intellij.openapi.ui.popup.JBPopupFactory
|
|||||||
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.PsiDocumentManager
|
||||||
import com.intellij.psi.PsiFile
|
|
||||||
import com.intellij.util.PlatformIcons
|
import com.intellij.util.PlatformIcons
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
@@ -41,7 +40,6 @@ import org.jetbrains.kotlin.psi.JetNamedFunction
|
|||||||
import org.jetbrains.kotlin.psi.JetParameterList
|
import org.jetbrains.kotlin.psi.JetParameterList
|
||||||
import org.jetbrains.kotlin.psi.JetPsiFactory
|
import org.jetbrains.kotlin.psi.JetPsiFactory
|
||||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||||
import org.jetbrains.kotlin.renderer.DescriptorRendererModifier
|
|
||||||
import org.jetbrains.kotlin.renderer.NameShortness
|
import org.jetbrains.kotlin.renderer.NameShortness
|
||||||
import org.jetbrains.kotlin.resolve.FunctionDescriptorUtil
|
import org.jetbrains.kotlin.resolve.FunctionDescriptorUtil
|
||||||
import org.jetbrains.kotlin.resolve.VisibilityUtil
|
import org.jetbrains.kotlin.resolve.VisibilityUtil
|
||||||
@@ -53,31 +51,47 @@ import java.util.*
|
|||||||
/**
|
/**
|
||||||
* Fix that changes member function's signature to match one of super functions' signatures.
|
* Fix that changes member function's signature to match one of super functions' signatures.
|
||||||
*/
|
*/
|
||||||
class ChangeMemberFunctionSignatureFix(element: JetNamedFunction) : JetHintAction<JetNamedFunction>(element) {
|
class ChangeMemberFunctionSignatureFix private constructor(
|
||||||
|
element: JetNamedFunction,
|
||||||
|
private val signatures: List<ChangeMemberFunctionSignatureFix.Signature>
|
||||||
|
) : JetHintAction<JetNamedFunction>(element) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
assert(signatures.isNotEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Signature(function: FunctionDescriptor) {
|
||||||
|
val sourceCode = SIGNATURE_SOURCE_RENDERER.render(function)
|
||||||
|
val preview = SIGNATURE_PREVIEW_RENDERER.render(function)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val SIGNATURE_SOURCE_RENDERER = IdeDescriptorRenderers.SOURCE_CODE.withOptions {
|
||||||
|
renderDefaultValues = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private val SIGNATURE_PREVIEW_RENDERER = DescriptorRenderer.withOptions {
|
||||||
|
typeNormalizer = IdeDescriptorRenderers.APPROXIMATE_FLEXIBLE_TYPES
|
||||||
|
withDefinedIn = false
|
||||||
|
modifiers = emptySet()
|
||||||
|
nameShortness = NameShortness.SHORT
|
||||||
|
unitReturnType = false
|
||||||
|
renderDefaultValues = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object : JetSingleIntentionActionFactory() {
|
companion object : JetSingleIntentionActionFactory() {
|
||||||
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
||||||
val function = diagnostic.psiElement as? JetNamedFunction ?: return null
|
val function = diagnostic.psiElement as? JetNamedFunction ?: return null
|
||||||
return ChangeMemberFunctionSignatureFix(function)
|
val signatures = computePossibleSignatures(function)
|
||||||
}
|
if (signatures.isEmpty()) return null
|
||||||
|
return ChangeMemberFunctionSignatureFix(function, signatures)
|
||||||
private val SIGNATURE_SOURCE_RENDERER = IdeDescriptorRenderers.SOURCE_CODE.withOptions {
|
|
||||||
renderDefaultValues = false
|
|
||||||
}
|
|
||||||
|
|
||||||
private val SIGNATURE_PREVIEW_RENDERER = DescriptorRenderer.withOptions {
|
|
||||||
typeNormalizer = IdeDescriptorRenderers.APPROXIMATE_FLEXIBLE_TYPES
|
|
||||||
withDefinedIn = false
|
|
||||||
modifiers = emptySet<DescriptorRendererModifier>()
|
|
||||||
nameShortness = NameShortness.SHORT
|
|
||||||
unitReturnType = false
|
|
||||||
renderDefaultValues = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes all the signatures a 'functionElement' could be changed to in order to remove NOTHING_TO_OVERRIDE error.
|
* Computes all the signatures a 'functionElement' could be changed to in order to remove NOTHING_TO_OVERRIDE error.
|
||||||
*/
|
*/
|
||||||
private fun computePossibleSignatures(functionElement: JetNamedFunction): List<FunctionDescriptor> {
|
private fun computePossibleSignatures(functionElement: JetNamedFunction): List<Signature> {
|
||||||
if (functionElement.valueParameterList == null) {
|
if (functionElement.valueParameterList == null) {
|
||||||
// we won't be able to modify its signature
|
// we won't be able to modify its signature
|
||||||
return emptyList()
|
return emptyList()
|
||||||
@@ -86,20 +100,17 @@ class ChangeMemberFunctionSignatureFix(element: JetNamedFunction) : JetHintActio
|
|||||||
val functionDescriptor = functionElement.resolveToDescriptor() as FunctionDescriptor
|
val functionDescriptor = functionElement.resolveToDescriptor() as FunctionDescriptor
|
||||||
val superFunctions = getPossibleSuperFunctionsDescriptors(functionDescriptor)
|
val superFunctions = getPossibleSuperFunctionsDescriptors(functionDescriptor)
|
||||||
|
|
||||||
val possibleSignatures: Map<String, FunctionDescriptor> = superFunctions
|
return superFunctions
|
||||||
.filter { it.kind.isReal }
|
.filter { it.kind.isReal }
|
||||||
.map { changeSignatureToMatch(functionDescriptor, it) }
|
.map { signatureToMatch(functionDescriptor, it) }
|
||||||
.toMapBy { SIGNATURE_PREVIEW_RENDERER.render(it) }
|
.distinctBy { it.sourceCode }
|
||||||
|
.sortedBy { it.preview }
|
||||||
return possibleSignatures.keys
|
|
||||||
.sorted()
|
|
||||||
.map { possibleSignatures[it]!! }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes function's signature to match superFunction's signature. Returns new descriptor.
|
* Changes function's signature to match superFunction's signature. Returns new descriptor.
|
||||||
*/
|
*/
|
||||||
private fun changeSignatureToMatch(function: FunctionDescriptor, superFunction: FunctionDescriptor): FunctionDescriptor {
|
private fun signatureToMatch(function: FunctionDescriptor, superFunction: FunctionDescriptor): Signature {
|
||||||
val superParameters = superFunction.valueParameters
|
val superParameters = superFunction.valueParameters
|
||||||
val parameters = function.valueParameters
|
val parameters = function.valueParameters
|
||||||
val newParameters = Lists.newArrayList(superParameters)
|
val newParameters = Lists.newArrayList(superParameters)
|
||||||
@@ -121,7 +132,8 @@ class ChangeMemberFunctionSignatureFix(element: JetNamedFunction) : JetHintActio
|
|||||||
/* copyOverrides = */ true),
|
/* copyOverrides = */ true),
|
||||||
newParameters)
|
newParameters)
|
||||||
newFunction.addOverriddenDescriptor(superFunction)
|
newFunction.addOverriddenDescriptor(superFunction)
|
||||||
return newFunction
|
|
||||||
|
return Signature(newFunction)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -182,27 +194,19 @@ class ChangeMemberFunctionSignatureFix(element: JetNamedFunction) : JetHintActio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val possibleSignatures = computePossibleSignatures(element)
|
|
||||||
|
|
||||||
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean {
|
|
||||||
return super.isAvailable(project, editor, file) && !possibleSignatures.isEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getText(): String {
|
override fun getText(): String {
|
||||||
val single = possibleSignatures.singleOrNull()
|
val single = signatures.singleOrNull()
|
||||||
if (single != null) {
|
return if (single != null)
|
||||||
return "Change function signature to '${SIGNATURE_PREVIEW_RENDERER.render(single)}'"
|
"Change function signature to '${single.preview}'"
|
||||||
}
|
else
|
||||||
else {
|
"Change function signature..."
|
||||||
return "Change function signature..."
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getFamilyName() = "Change function signature"
|
override fun getFamilyName() = "Change function signature"
|
||||||
|
|
||||||
override fun invoke(project: Project, editor: Editor?, file: JetFile) {
|
override fun invoke(project: Project, editor: Editor?, file: JetFile) {
|
||||||
CommandProcessor.getInstance().runUndoTransparentAction {
|
CommandProcessor.getInstance().runUndoTransparentAction {
|
||||||
MyAction(project, editor, element, possibleSignatures).execute()
|
MyAction(project, editor, element, signatures).execute()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,15 +239,13 @@ class ChangeMemberFunctionSignatureFix(element: JetNamedFunction) : JetHintActio
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showHint(editor: Editor): Boolean {
|
override fun showHint(editor: Editor) = !HintManager.getInstance().hasShownHintsThatWillHideByOtherHint(true)
|
||||||
return possibleSignatures.isNotEmpty() && !HintManager.getInstance().hasShownHintsThatWillHideByOtherHint(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MyAction(
|
private class MyAction(
|
||||||
private val project: Project,
|
private val project: Project,
|
||||||
private val editor: Editor?,
|
private val editor: Editor?,
|
||||||
private val function: JetNamedFunction,
|
private val function: JetNamedFunction,
|
||||||
private val signatures: List<FunctionDescriptor>
|
private val signatures: List<Signature>
|
||||||
) {
|
) {
|
||||||
fun execute() {
|
fun execute() {
|
||||||
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
||||||
@@ -258,31 +260,29 @@ class ChangeMemberFunctionSignatureFix(element: JetNamedFunction) : JetHintActio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val signaturePopup: BaseListPopupStep<FunctionDescriptor>
|
private val signaturePopup: BaseListPopupStep<Signature>
|
||||||
get() {
|
get() {
|
||||||
return object : BaseListPopupStep<FunctionDescriptor>("Choose Signature", signatures) {
|
return object : BaseListPopupStep<Signature>("Choose Signature", signatures) {
|
||||||
override fun isAutoSelectionEnabled() = false
|
override fun isAutoSelectionEnabled() = false
|
||||||
|
|
||||||
override fun onChosen(selectedValue: FunctionDescriptor, finalChoice: Boolean): PopupStep<Any>? {
|
override fun onChosen(selectedValue: Signature, finalChoice: Boolean): PopupStep<Any>? {
|
||||||
if (finalChoice) {
|
if (finalChoice) {
|
||||||
changeSignature(selectedValue)
|
changeSignature(selectedValue)
|
||||||
}
|
}
|
||||||
return PopupStep.FINAL_CHOICE
|
return PopupStep.FINAL_CHOICE
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getIconFor(aValue: FunctionDescriptor) = PlatformIcons.FUNCTION_ICON
|
override fun getIconFor(aValue: Signature) = PlatformIcons.FUNCTION_ICON
|
||||||
|
|
||||||
override fun getTextFor(aValue: FunctionDescriptor) = SIGNATURE_PREVIEW_RENDERER.render(aValue)
|
override fun getTextFor(aValue: Signature) = aValue.preview
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun changeSignature(patternDescriptor: FunctionDescriptor) {
|
private fun changeSignature(signature: Signature) {
|
||||||
val signatureString = SIGNATURE_SOURCE_RENDERER.render(patternDescriptor)
|
|
||||||
|
|
||||||
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
||||||
|
|
||||||
project.executeWriteCommand("Change Function Signature") {
|
project.executeWriteCommand("Change Function Signature") {
|
||||||
val patternFunction = JetPsiFactory(project).createFunction(signatureString)
|
val patternFunction = JetPsiFactory(project).createFunction(signature.sourceCode)
|
||||||
|
|
||||||
val newTypeRef = function.setTypeReference(patternFunction.typeReference)
|
val newTypeRef = function.setTypeReference(patternFunction.typeReference)
|
||||||
if (newTypeRef != null) {
|
if (newTypeRef != null) {
|
||||||
|
|||||||
Reference in New Issue
Block a user