Converted to Kotlin

This commit is contained in:
Valentin Kipyatkov
2015-10-15 19:36:10 +03:00
parent 714e97751d
commit cd6450e1a1
2 changed files with 336 additions and 414 deletions
@@ -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<JetNamedFunction> {
private static final DescriptorRenderer SIGNATURE_SOURCE_RENDERER = IdeDescriptorRenderers.SOURCE_CODE.withOptions(
new Function1<DescriptorRendererOptions, Unit>() {
@Override
public Unit invoke(DescriptorRendererOptions options) {
options.setRenderDefaultValues(false);
return Unit.INSTANCE$;
}
}
);
private static final DescriptorRenderer SIGNATURE_PREVIEW_RENDERER = DescriptorRenderer.Companion.withOptions(
new Function1<DescriptorRendererOptions, Unit>() {
@Override
public Unit invoke(DescriptorRendererOptions options) {
options.setTypeNormalizer(IdeDescriptorRenderers.APPROXIMATE_FLEXIBLE_TYPES);
options.setWithDefinedIn(false);
options.setModifiers(Collections.<DescriptorRendererModifier>emptySet());
options.setNameShortness(NameShortness.SHORT);
options.setUnitReturnType(false);
options.setRenderDefaultValues(false);
return Unit.INSTANCE$;
}
}
);
private final List<FunctionDescriptor> 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<FunctionDescriptor> 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<FunctionDescriptor> superFunctions = getPossibleSuperFunctionsDescriptors(functionDescriptor);
final Map<String, FunctionDescriptor> 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<String> keys = new ArrayList<String>(possibleSignatures.keySet());
Collections.sort(keys);
return new ArrayList<FunctionDescriptor>(Collections2.transform(keys, new Function<String, FunctionDescriptor>() {
@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<ValueParameterDescriptor> superParameters = superFunction.getValueParameters();
List<ValueParameterDescriptor> parameters = function.getValueParameters();
List<ValueParameterDescriptor> 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<CallableMemberDescriptor> descriptors =
Queues.<CallableMemberDescriptor>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<ValueParameterDescriptor> superParameters,
@NotNull List<ValueParameterDescriptor> parameters,
@NotNull List<ValueParameterDescriptor> 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<FunctionDescriptor> getPossibleSuperFunctionsDescriptors(@NotNull FunctionDescriptor functionDescriptor) {
DeclarationDescriptor containingDeclaration = functionDescriptor.getContainingDeclaration();
List<FunctionDescriptor> 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<FunctionDescriptor> signatures;
public MyAction(
@NotNull Project project,
@NotNull Editor editor,
@NotNull JetNamedFunction function,
@NotNull List<FunctionDescriptor> 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<FunctionDescriptor>(
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);
}
}
}
@@ -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<JetNamedFunction>(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<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.
*/
private fun computePossibleSignatures(functionElement: JetNamedFunction): List<FunctionDescriptor> {
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<String, FunctionDescriptor>()
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<String, FunctionDescriptor> {
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<CallableMemberDescriptor>(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<ValueParameterDescriptor>,
parameters: List<ValueParameterDescriptor>,
newParameters: MutableList<ValueParameterDescriptor>,
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<FunctionDescriptor> {
val containingDeclaration = functionDescriptor.containingDeclaration
val superFunctions = Lists.newArrayList<FunctionDescriptor>()
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<FunctionDescriptor>) {
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<FunctionDescriptor>
get() {
return object : BaseListPopupStep<FunctionDescriptor>(JetBundle.message("change.function.signature.chooser.title"), signatures) {
override fun isAutoSelectionEnabled(): Boolean {
return false
}
override fun onChosen(selectedValue: FunctionDescriptor, finalChoice: Boolean): PopupStep<Any>? {
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!!)
}
}
}