From cd6450e1a1f1b6fa138627be8fb7f225dc32bc23 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Thu, 15 Oct 2015 19:36:10 +0300 Subject: [PATCH] Converted to Kotlin --- .../ChangeMemberFunctionSignatureFix.java | 414 ------------------ .../ChangeMemberFunctionSignatureFix.kt | 336 ++++++++++++++ 2 files changed, 336 insertions(+), 414 deletions(-) delete mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeMemberFunctionSignatureFix.java create mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeMemberFunctionSignatureFix.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeMemberFunctionSignatureFix.java b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeMemberFunctionSignatureFix.java deleted file mode 100644 index 150c4aae2fc..00000000000 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeMemberFunctionSignatureFix.java +++ /dev/null @@ -1,414 +0,0 @@ -/* - * Copyright 2010-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.kotlin.idea.quickfix; - -import com.google.common.base.Function; -import com.google.common.collect.Collections2; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Queues; -import com.intellij.codeInsight.hint.HintManager; -import com.intellij.codeInsight.intention.IntentionAction; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.command.CommandProcessor; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.popup.JBPopupFactory; -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.IncorrectOperationException; -import com.intellij.util.PlatformIcons; -import kotlin.Unit; -import kotlin.jvm.functions.Function1; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.descriptors.*; -import org.jetbrains.kotlin.diagnostics.Diagnostic; -import org.jetbrains.kotlin.idea.JetBundle; -import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils; -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.incremental.components.NoLookupLocation; -import org.jetbrains.kotlin.name.Name; -import org.jetbrains.kotlin.psi.*; -import org.jetbrains.kotlin.renderer.DescriptorRenderer; -import org.jetbrains.kotlin.renderer.DescriptorRendererModifier; -import org.jetbrains.kotlin.renderer.DescriptorRendererOptions; -import org.jetbrains.kotlin.renderer.NameShortness; -import org.jetbrains.kotlin.resolve.FunctionDescriptorUtil; -import org.jetbrains.kotlin.resolve.VisibilityUtil; -import org.jetbrains.kotlin.resolve.scopes.JetScope; -import org.jetbrains.kotlin.types.JetType; -import org.jetbrains.kotlin.types.TypeUtils; -import org.jetbrains.kotlin.types.checker.JetTypeChecker; - -import javax.swing.*; -import java.util.*; - -/** - * Fix that changes member function's signature to match one of super functions' signatures. - */ -public class ChangeMemberFunctionSignatureFix extends JetHintAction { - private static final DescriptorRenderer SIGNATURE_SOURCE_RENDERER = IdeDescriptorRenderers.SOURCE_CODE.withOptions( - new Function1() { - @Override - public Unit invoke(DescriptorRendererOptions options) { - options.setRenderDefaultValues(false); - return Unit.INSTANCE$; - } - } - ); - - private static final DescriptorRenderer SIGNATURE_PREVIEW_RENDERER = DescriptorRenderer.Companion.withOptions( - new Function1() { - @Override - public Unit invoke(DescriptorRendererOptions options) { - options.setTypeNormalizer(IdeDescriptorRenderers.APPROXIMATE_FLEXIBLE_TYPES); - options.setWithDefinedIn(false); - options.setModifiers(Collections.emptySet()); - options.setNameShortness(NameShortness.SHORT); - options.setUnitReturnType(false); - options.setRenderDefaultValues(false); - return Unit.INSTANCE$; - } - } - ); - - private final List possibleSignatures; - - public ChangeMemberFunctionSignatureFix(@NotNull JetNamedFunction element) { - super(element); - this.possibleSignatures = computePossibleSignatures(element); - } - - @Override - public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { - return super.isAvailable(project, editor, file) && !possibleSignatures.isEmpty(); - } - - @NotNull - @Override - public String getText() { - if (possibleSignatures.size() == 1) { - return JetBundle.message("change.function.signature.action.single", SIGNATURE_PREVIEW_RENDERER.render(possibleSignatures.get(0))); - } - else { - return JetBundle.message("change.function.signature.action.multiple"); - } - } - - @NotNull - @Override - public String getFamilyName() { - return JetBundle.message("change.function.signature.family"); - } - - @Override - protected void invoke(@NotNull final Project project, final Editor editor, JetFile file) throws IncorrectOperationException { - CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() { - @Override - public void run() { - new MyAction(project, editor, getElement(), possibleSignatures).execute(); - } - }); - } - - /** - * Computes all the signatures a 'functionElement' could be changed to in order to remove NOTHING_TO_OVERRIDE error. - */ - @NotNull - private static List computePossibleSignatures(@NotNull JetNamedFunction functionElement) { - if (functionElement.getValueParameterList() == null) { // we won't be able to modify its signature - return Collections.emptyList(); - } - - FunctionDescriptor functionDescriptor = (FunctionDescriptor) ResolutionUtils.resolveToDescriptor(functionElement); - List superFunctions = getPossibleSuperFunctionsDescriptors(functionDescriptor); - final Map possibleSignatures = Maps.newHashMap(); - for (FunctionDescriptor superFunction : superFunctions) { - if (!superFunction.getKind().isReal()) continue; - FunctionDescriptor signature = changeSignatureToMatch(functionDescriptor, superFunction); - possibleSignatures.put(SIGNATURE_PREVIEW_RENDERER.render(signature), signature); - } - List keys = new ArrayList(possibleSignatures.keySet()); - Collections.sort(keys); - return new ArrayList(Collections2.transform(keys, new Function() { - @Override - public FunctionDescriptor apply(String key) { - return possibleSignatures.get(key); - } - })); - } - - /** - * Changes function's signature to match superFunction's signature. Returns new descriptor. - */ - private static FunctionDescriptor changeSignatureToMatch(FunctionDescriptor function, FunctionDescriptor superFunction) { - List superParameters = superFunction.getValueParameters(); - List parameters = function.getValueParameters(); - List newParameters = Lists.newArrayList(superParameters); - - // Parameters in superFunction, which are matched in new function signature: - BitSet matched = new BitSet(superParameters.size()); - // Parameters in this function, which are used in new function signature: - BitSet used = new BitSet(superParameters.size()); - - matchParameters(MATCH_NAMES, superParameters, parameters, newParameters, matched, used); - matchParameters(MATCH_TYPES, superParameters, parameters, newParameters, matched, used); - - FunctionDescriptor newFunction = FunctionDescriptorUtil.replaceFunctionParameters( - superFunction.copy( - function.getContainingDeclaration(), - Modality.OPEN, - getVisibility(function, superFunction), - CallableMemberDescriptor.Kind.DELEGATION, - /* copyOverrides = */ true), - newParameters); - newFunction.addOverriddenDescriptor(superFunction); - return newFunction; - } - - /** - * Returns new visibility for 'function' modified to override 'superFunction'. - */ - private static Visibility getVisibility(FunctionDescriptor function, FunctionDescriptor superFunction) { - ArrayDeque descriptors = - Queues.newArrayDeque(Arrays.asList(superFunction, function)); - return VisibilityUtil.findMemberWithMaxVisibility(descriptors).getVisibility(); - } - - /** Helper interface for matchParameters(..) method. */ - private interface ParameterChooser { - /** - * Checks if 'parameter' may be used to match 'superParameter'. - * If so, returns (possibly modified) descriptor to be used as the new parameter. - * If not, returns null. - */ - @Nullable - ValueParameterDescriptor choose(@NotNull ValueParameterDescriptor parameter, @NotNull ValueParameterDescriptor superParameter); - } - - private static final ParameterChooser MATCH_NAMES = new ParameterChooser() { - @Nullable - @Override - public ValueParameterDescriptor choose( - @NotNull ValueParameterDescriptor parameter, - @NotNull ValueParameterDescriptor superParameter - ) { - return parameter.getName().equals(superParameter.getName()) ? superParameter : null; - } - }; - - private static final ParameterChooser MATCH_TYPES = new ParameterChooser() { - @Nullable - @Override - public ValueParameterDescriptor choose( - @NotNull ValueParameterDescriptor parameter, - @NotNull ValueParameterDescriptor superParameter - ) { - // TODO: support for generic functions - if (JetTypeChecker.DEFAULT.equalTypes(parameter.getType(), superParameter.getType())) { - return superParameter.copy(parameter.getContainingDeclaration(), parameter.getName()); - } - else { - return null; - } - } - }; - - /** - * Match function's parameters with super function's parameters using parameterChooser. - * Doesn't have to preserve ordering, parameter names or types. - * @param superParameters - super function's parameters - * @param parameters - function's parameters - * @param newParameters - new parameters (may be modified by this function) - * @param matched - true iff this parameter in super function is matched by some parameter in function (may be modified by this function) - * @param used - true iff this parameter in function is used to match some parameter in super function (may be modified by this function) - */ - private static void matchParameters( - @NotNull ParameterChooser parameterChooser, - @NotNull List superParameters, - @NotNull List parameters, - @NotNull List newParameters, - @NotNull BitSet matched, - @NotNull BitSet used - ) { - for (ValueParameterDescriptor superParameter : superParameters) { - if (!matched.get(superParameter.getIndex())) { - for (ValueParameterDescriptor parameter : parameters) { - ValueParameterDescriptor choice = parameterChooser.choose(parameter, superParameter); - if (!used.get(parameter.getIndex()) && choice != null) { - used.set(parameter.getIndex(), true); - matched.set(superParameter.getIndex(), true); - newParameters.set(superParameter.getIndex(), choice); - break; - } - } - } - } - } - - /** - * Returns all open functions in superclasses which have the same name as 'functionDescriptor' (but possibly - * different parameters/return type). - */ - @NotNull - private static List getPossibleSuperFunctionsDescriptors(@NotNull FunctionDescriptor functionDescriptor) { - DeclarationDescriptor containingDeclaration = functionDescriptor.getContainingDeclaration(); - List superFunctions = Lists.newArrayList(); - if (!(containingDeclaration instanceof ClassDescriptor)) return superFunctions; - ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration; - - Name name = functionDescriptor.getName(); - for (JetType type : TypeUtils.getAllSupertypes(classDescriptor.getDefaultType())) { - JetScope scope = type.getMemberScope(); - for (FunctionDescriptor function : scope.getFunctions(name, NoLookupLocation.FROM_IDE)) { - if (!function.getKind().isReal()) continue; - if (function.getModality().isOverridable()) - superFunctions.add(function); - } - } - return superFunctions; - } - - @NotNull - public static JetSingleIntentionActionFactory createFactory() { - return new JetSingleIntentionActionFactory() { - @Nullable - @Override - public IntentionAction createAction(Diagnostic diagnostic) { - JetNamedFunction function = QuickFixUtil.getParentElementOfType(diagnostic, JetNamedFunction.class); - return function == null ? null : new ChangeMemberFunctionSignatureFix(function); - } - }; - } - - @Override - public boolean showHint(@NotNull Editor editor) { - if (possibleSignatures.isEmpty()) { - return false; - } - - Project project = editor.getProject(); - if (project == null) { - return false; - } - - if (HintManager.getInstance().hasShownHintsThatWillHideByOtherHint(true)) { - return false; - } - - return true; - } - - private static class MyAction { - private final Project project; - private final Editor editor; - private final JetNamedFunction function; - private final List signatures; - - public MyAction( - @NotNull Project project, - @NotNull Editor editor, - @NotNull JetNamedFunction function, - @NotNull List signatures - ) { - this.project = project; - this.editor = editor; - this.function = function; - this.signatures = signatures; - } - - public boolean execute() { - PsiDocumentManager.getInstance(project).commitAllDocuments(); - - if (!function.isValid() || signatures.isEmpty()) return false; - - if (signatures.size() == 1 || !editor.getComponent().isShowing()) { - changeSignature(signatures.get(0)); - } - else { - chooseSignatureAndChange(); - } - - return true; - } - - private BaseListPopupStep getSignaturePopup() { - return new BaseListPopupStep( - JetBundle.message("change.function.signature.chooser.title"), signatures) { - @Override - public boolean isAutoSelectionEnabled() { - return false; - } - - @Override - public PopupStep onChosen(FunctionDescriptor selectedValue, boolean finalChoice) { - if (finalChoice) { - changeSignature(selectedValue); - } - return FINAL_CHOICE; - } - - @Override - public Icon getIconFor(FunctionDescriptor aValue) { - return PlatformIcons.FUNCTION_ICON; - } - - @NotNull - @Override - public String getTextFor(FunctionDescriptor aValue) { - return SIGNATURE_PREVIEW_RENDERER.render(aValue); - } - }; - } - - private void changeSignature(FunctionDescriptor patternDescriptor) { - final String signatureString = SIGNATURE_SOURCE_RENDERER.render(patternDescriptor); - - PsiDocumentManager.getInstance(project).commitAllDocuments(); - - final JetPsiFactory psiFactory = JetPsiFactoryKt.JetPsiFactory(project); - CommandProcessor.getInstance().executeCommand(project, new Runnable() { - @Override - public void run() { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - JetNamedFunction patternFunction = psiFactory.createFunction(signatureString); - - JetTypeReference newTypeRef = function.setTypeReference(patternFunction.getTypeReference()); - if (newTypeRef != null) { - ShortenReferences.DEFAULT.process(newTypeRef); - } - - JetParameterList newParameterList = (JetParameterList) function.getValueParameterList().replace(patternFunction.getValueParameterList()); - ShortenReferences.DEFAULT.process(newParameterList); - } - }); - } - }, JetBundle.message("change.function.signature.action"), null); - } - - private void chooseSignatureAndChange() { - JBPopupFactory.getInstance().createListPopup(getSignaturePopup()).showInBestPositionFor(editor); - } - } -} diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeMemberFunctionSignatureFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeMemberFunctionSignatureFix.kt new file mode 100644 index 00000000000..27b7d718f9e --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeMemberFunctionSignatureFix.kt @@ -0,0 +1,336 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.quickfix + +import com.google.common.base.Function +import com.google.common.collect.Collections2 +import com.google.common.collect.Lists +import com.google.common.collect.Maps +import com.google.common.collect.Queues +import com.intellij.codeInsight.hint.HintManager +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.openapi.command.CommandProcessor +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.popup.JBPopupFactory +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.descriptors.* +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.idea.JetBundle +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor +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 +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.psi.JetFile +import org.jetbrains.kotlin.psi.JetNamedFunction +import org.jetbrains.kotlin.psi.JetParameterList +import org.jetbrains.kotlin.psi.JetPsiFactory +import org.jetbrains.kotlin.renderer.DescriptorRenderer +import org.jetbrains.kotlin.renderer.DescriptorRendererModifier +import org.jetbrains.kotlin.renderer.NameShortness +import org.jetbrains.kotlin.resolve.FunctionDescriptorUtil +import org.jetbrains.kotlin.resolve.VisibilityUtil +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.checker.JetTypeChecker +import java.util.* +import javax.swing.Icon + +/** + * Fix that changes member function's signature to match one of super functions' signatures. + */ +class ChangeMemberFunctionSignatureFix(element: JetNamedFunction) : JetHintAction(element) { + + 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 + } + + /** + * Computes all the signatures a 'functionElement' could be changed to in order to remove NOTHING_TO_OVERRIDE error. + */ + private fun computePossibleSignatures(functionElement: JetNamedFunction): List { + if (functionElement.valueParameterList == null) { + // we won't be able to modify its signature + return emptyList() + } + + val functionDescriptor = functionElement.resolveToDescriptor() as FunctionDescriptor + val superFunctions = getPossibleSuperFunctionsDescriptors(functionDescriptor) + val possibleSignatures = Maps.newHashMap() + for (superFunction in superFunctions) { + if (!superFunction.kind.isReal) continue + val signature = changeSignatureToMatch(functionDescriptor, superFunction) + possibleSignatures.put(SIGNATURE_PREVIEW_RENDERER.render(signature), signature) + } + val keys = ArrayList(possibleSignatures.keys) + Collections.sort(keys) + return ArrayList(Collections2.transform(keys, object : Function { + override fun apply(key: String?): FunctionDescriptor? { + return possibleSignatures.get(key) + } + })) + } + + /** + * Changes function's signature to match superFunction's signature. Returns new descriptor. + */ + private fun changeSignatureToMatch(function: FunctionDescriptor, superFunction: FunctionDescriptor): FunctionDescriptor { + val superParameters = superFunction.valueParameters + val parameters = function.valueParameters + val newParameters = Lists.newArrayList(superParameters) + + // Parameters in superFunction, which are matched in new function signature: + val matched = BitSet(superParameters.size) + // Parameters in this function, which are used in new function signature: + val used = BitSet(superParameters.size) + + matchParameters(MATCH_NAMES, superParameters, parameters, newParameters, matched, used) + matchParameters(MATCH_TYPES, superParameters, parameters, newParameters, matched, used) + + val newFunction = FunctionDescriptorUtil.replaceFunctionParameters( + superFunction.copy( + function.containingDeclaration, + Modality.OPEN, + getVisibility(function, superFunction), + CallableMemberDescriptor.Kind.DELEGATION, + /* copyOverrides = */ true), + newParameters) + newFunction.addOverriddenDescriptor(superFunction) + return newFunction + } + + /** + * Returns new visibility for 'function' modified to override 'superFunction'. + */ + private fun getVisibility(function: FunctionDescriptor, superFunction: FunctionDescriptor): Visibility { + val descriptors = Queues.newArrayDeque(Arrays.asList(superFunction, function)) + return VisibilityUtil.findMemberWithMaxVisibility(descriptors).visibility + } + + private val MATCH_NAMES = object : ParameterChooser { + override fun choose( + parameter: ValueParameterDescriptor, + superParameter: ValueParameterDescriptor): ValueParameterDescriptor? { + return if (parameter.name == superParameter.name) superParameter else null + } + } + + private val MATCH_TYPES = object : ParameterChooser { + override fun choose( + parameter: ValueParameterDescriptor, + superParameter: ValueParameterDescriptor): ValueParameterDescriptor? { + // TODO: support for generic functions + if (JetTypeChecker.DEFAULT.equalTypes(parameter.type, superParameter.type)) { + return superParameter.copy(parameter.containingDeclaration, parameter.name) + } + else { + return null + } + } + } + + /** + * Match function's parameters with super function's parameters using parameterChooser. + * Doesn't have to preserve ordering, parameter names or types. + * @param superParameters - super function's parameters + * * + * @param parameters - function's parameters + * * + * @param newParameters - new parameters (may be modified by this function) + * * + * @param matched - true iff this parameter in super function is matched by some parameter in function (may be modified by this function) + * * + * @param used - true iff this parameter in function is used to match some parameter in super function (may be modified by this function) + */ + private fun matchParameters( + parameterChooser: ParameterChooser, + superParameters: List, + parameters: List, + newParameters: MutableList, + matched: BitSet, + used: BitSet) { + for (superParameter in superParameters) { + if (!matched.get(superParameter.index)) { + for (parameter in parameters) { + val choice = parameterChooser.choose(parameter, superParameter) + if (!used.get(parameter.index) && choice != null) { + used.set(parameter.index, true) + matched.set(superParameter.index, true) + newParameters.set(superParameter.index, choice) + break + } + } + } + } + } + + /** + * Returns all open functions in superclasses which have the same name as 'functionDescriptor' (but possibly + * different parameters/return type). + */ + private fun getPossibleSuperFunctionsDescriptors(functionDescriptor: FunctionDescriptor): List { + val containingDeclaration = functionDescriptor.containingDeclaration + val superFunctions = Lists.newArrayList() + if (containingDeclaration !is ClassDescriptor) return superFunctions + + val name = functionDescriptor.name + for (type in TypeUtils.getAllSupertypes(containingDeclaration.defaultType)) { + val scope = type.memberScope + for (function in scope.getFunctions(name, NoLookupLocation.FROM_IDE)) { + if (!function.kind.isReal) continue + if (function.modality.isOverridable) + superFunctions.add(function) + } + } + return superFunctions + } + + fun createFactory(): JetSingleIntentionActionFactory { + return object : JetSingleIntentionActionFactory() { + public override fun createAction(diagnostic: Diagnostic): IntentionAction? { + val function = QuickFixUtil.getParentElementOfType(diagnostic, JetNamedFunction::class.java) + return if (function == null) null else ChangeMemberFunctionSignatureFix(function) + } + } + } + } + + 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 { + if (possibleSignatures.size == 1) { + return JetBundle.message("change.function.signature.action.single", SIGNATURE_PREVIEW_RENDERER.render(possibleSignatures.get(0))) + } + else { + return JetBundle.message("change.function.signature.action.multiple") + } + } + + override fun getFamilyName(): String { + return JetBundle.message("change.function.signature.family") + } + + override fun invoke(project: Project, editor: Editor?, file: JetFile) { + CommandProcessor.getInstance().runUndoTransparentAction(object : Runnable { + override fun run() { + MyAction(project, editor, element, possibleSignatures).execute() + } + }) + } + + /** Helper interface for matchParameters(..) method. */ + private interface ParameterChooser { + /** + * Checks if 'parameter' may be used to match 'superParameter'. + * If so, returns (possibly modified) descriptor to be used as the new parameter. + * If not, returns null. + */ + fun choose(parameter: ValueParameterDescriptor, superParameter: ValueParameterDescriptor): ValueParameterDescriptor? + } + + override fun showHint(editor: Editor): Boolean { + return possibleSignatures.isNotEmpty() && !HintManager.getInstance().hasShownHintsThatWillHideByOtherHint(true) + } + + private class MyAction( + private val project: Project, + private val editor: Editor?, + private val function: JetNamedFunction, + private val signatures: List) { + + fun execute(): Boolean { + PsiDocumentManager.getInstance(project).commitAllDocuments() + + if (!function.isValid || signatures.isEmpty()) return false + + if (signatures.size == 1 || editor == null || !editor.component.isShowing) { + changeSignature(signatures.get(0)) + } + else { + chooseSignatureAndChange() + } + + return true + } + + private val signaturePopup: BaseListPopupStep + get() { + return object : BaseListPopupStep(JetBundle.message("change.function.signature.chooser.title"), signatures) { + override fun isAutoSelectionEnabled(): Boolean { + return false + } + + override fun onChosen(selectedValue: FunctionDescriptor, finalChoice: Boolean): PopupStep? { + if (finalChoice) { + changeSignature(selectedValue) + } + return PopupStep.FINAL_CHOICE + } + + override fun getIconFor(aValue: FunctionDescriptor): Icon? { + return PlatformIcons.FUNCTION_ICON + } + + override fun getTextFor(aValue: FunctionDescriptor): String { + return SIGNATURE_PREVIEW_RENDERER.render(aValue) + } + } + } + + private fun changeSignature(patternDescriptor: FunctionDescriptor) { + val signatureString = SIGNATURE_SOURCE_RENDERER.render(patternDescriptor) + + PsiDocumentManager.getInstance(project).commitAllDocuments() + + val psiFactory = JetPsiFactory(project) + project.executeWriteCommand(JetBundle.message("change.function.signature.action")) { + val patternFunction = psiFactory.createFunction(signatureString) + + val newTypeRef = function.setTypeReference(patternFunction.typeReference) + if (newTypeRef != null) { + ShortenReferences.DEFAULT.process(newTypeRef) + } + + val newParameterList = function.valueParameterList!!.replace(patternFunction.valueParameterList!!) as JetParameterList + ShortenReferences.DEFAULT.process(newParameterList) + } + } + + private fun chooseSignatureAndChange() { + JBPopupFactory.getInstance().createListPopup(signaturePopup).showInBestPositionFor(editor!!) + } + } +}