Add new functionality to "Change signature" refactoring

Extract single point of entry for all change signature refactorings and fixes (remove parameter, add parameter)
Change signature now affects overriding functions as well
Ask the user whether he wants to refactor base function(s) or the selected one if appropiate
Fix a problem with descriptorToDeclaration in JetChangeSignatureHandler
Rename: JetFunctionPlatformDescriptor -> JetMethodDescriptor
This commit is contained in:
Pavel V. Talanov
2013-10-07 20:49:44 +04:00
parent 967030e7c1
commit 8c95884ad2
27 changed files with 1198 additions and 323 deletions
@@ -357,4 +357,35 @@ public class BindingContextUtils {
}
return OverridingUtil.filterOutOverridden(result);
}
@NotNull
public static Set<FunctionDescriptor> getDirectlyOverriddenDeclarations(@NotNull FunctionDescriptor descriptor) {
//noinspection unchecked
return (Set) getDirectlyOverriddenDeclarations((CallableMemberDescriptor) descriptor);
}
@NotNull
public static Set<PropertyDescriptor> getDirectlyOverriddenDeclarations(@NotNull PropertyDescriptor descriptor) {
//noinspection unchecked
return (Set) getDirectlyOverriddenDeclarations((CallableMemberDescriptor) descriptor);
}
@NotNull
public static Set<FunctionDescriptor> getAllOverriddenDeclarations(@NotNull FunctionDescriptor functionDescriptor) {
Set<FunctionDescriptor> result = Sets.newHashSet();
for (FunctionDescriptor overriddenDeclaration : functionDescriptor.getOverriddenDescriptors()) {
CallableMemberDescriptor.Kind kind = overriddenDeclaration.getKind();
if (kind == DECLARATION) {
result.add(overriddenDeclaration);
}
else if (kind == DELEGATION || kind == FAKE_OVERRIDE || kind == SYNTHESIZED) {
//do nothing
}
else {
throw new AssertionError("Unexpected callable kind " + kind);
}
result.addAll(getAllOverriddenDeclarations(overriddenDeclaration));
}
return result;
}
}
@@ -16,7 +16,6 @@
package org.jetbrains.jet.plugin.quickfix;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
@@ -33,13 +32,13 @@ import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
import org.jetbrains.jet.plugin.JetBundle;
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
import org.jetbrains.jet.plugin.refactoring.JetNameValidator;
import org.jetbrains.jet.plugin.refactoring.changeSignature.JetChangeSignatureDialog;
import org.jetbrains.jet.plugin.refactoring.changeSignature.JetFunctionPlatformDescriptorImpl;
import org.jetbrains.jet.plugin.refactoring.changeSignature.JetParameterInfo;
import org.jetbrains.jet.plugin.refactoring.changeSignature.*;
import org.jetbrains.jet.renderer.DescriptorRenderer;
import java.util.List;
import static org.jetbrains.jet.plugin.refactoring.changeSignature.ChangeSignaturePackage.runChangeSignature;
public class AddFunctionParametersFix extends ChangeFunctionSignatureFix {
private final JetCallElement callElement;
private final boolean hasTypeMismatches;
@@ -85,45 +84,43 @@ public class AddFunctionParametersFix extends ChangeFunctionSignatureFix {
@Override
protected void invoke(@NotNull Project project, Editor editor, JetFile file) {
BindingContext bindingContext = AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) callElement.getContainingFile()).getBindingContext();
JetFunctionPlatformDescriptorImpl platformDescriptor = new JetFunctionPlatformDescriptorImpl(functionDescriptor, element);
final List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
List<? extends ValueArgument> arguments = callElement.getValueArguments();
JetNameValidator validator = JetNameValidator.getCollectingValidator(callElement.getProject());
boolean performSilently = !hasTypeMismatches && !(functionDescriptor instanceof ConstructorDescriptor) && !hasOtherUsages();
runChangeSignature(project, functionDescriptor, addParameterConfiguration(), bindingContext, callElement, getText(), performSilently);
}
for (int i = 0; i < arguments.size(); i ++) {
ValueArgument argument = arguments.get(i);
JetExpression expression = argument.getArgumentExpression();
if (i < parameters.size()) {
validator.validateName(parameters.get(i).getName().asString());
JetType argumentType = expression != null ? bindingContext.get(BindingContext.EXPRESSION_TYPE, expression) : null;
JetType parameterType = parameters.get(i).getType();
if (argumentType != null && !JetTypeChecker.INSTANCE.isSubtypeOf(argumentType, parameterType))
platformDescriptor.getParameters().get(i).setTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(argumentType));
}
else {
JetParameterInfo parameterInfo = getNewParameterInfo(bindingContext, argument, validator);
if (expression != null)
parameterInfo.setDefaultValueText(expression.getText());
platformDescriptor.addParameter(parameterInfo);
}
}
JetChangeSignatureDialog dialog = new JetChangeSignatureDialog(project, platformDescriptor, callElement, getText()) {
private JetChangeSignatureConfiguration addParameterConfiguration() {
return new JetChangeSignatureConfiguration() {
@Override
protected int getSelectedIdx() {
return parameters.size();
public void configure(
@NotNull JetChangeSignatureData changeSignatureData, @NotNull BindingContext bindingContext
) {
List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
List<? extends ValueArgument> arguments = callElement.getValueArguments();
JetNameValidator validator = JetNameValidator.getCollectingValidator(callElement.getProject());
for (int i = 0; i < arguments.size(); i ++) {
ValueArgument argument = arguments.get(i);
JetExpression expression = argument.getArgumentExpression();
if (i < parameters.size()) {
validator.validateName(parameters.get(i).getName().asString());
JetType argumentType = expression != null ? bindingContext.get(BindingContext.EXPRESSION_TYPE, expression) : null;
JetType parameterType = parameters.get(i).getType();
if (argumentType != null && !JetTypeChecker.INSTANCE.isSubtypeOf(argumentType, parameterType))
changeSignatureData.getParameters().get(i).setTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(argumentType));
}
else {
JetParameterInfo parameterInfo = getNewParameterInfo(bindingContext, argument, validator);
if (expression != null)
parameterInfo.setDefaultValueText(expression.getText());
changeSignatureData.addParameter(parameterInfo);
}
}
}
};
if (ApplicationManager.getApplication().isUnitTestMode() ||
!hasTypeMismatches && !(functionDescriptor instanceof ConstructorDescriptor) && !hasOtherUsages())
performRefactoringSilently(dialog);
else
dialog.show();
}
private boolean hasOtherUsages() {
@@ -22,15 +22,20 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetFunctionLiteral;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.plugin.JetBundle;
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
import org.jetbrains.jet.plugin.refactoring.JetNameSuggester;
import org.jetbrains.jet.plugin.refactoring.JetNameValidator;
import org.jetbrains.jet.plugin.refactoring.changeSignature.JetFunctionPlatformDescriptorImpl;
import org.jetbrains.jet.plugin.refactoring.changeSignature.JetChangeSignatureConfiguration;
import org.jetbrains.jet.plugin.refactoring.changeSignature.JetChangeSignatureData;
import org.jetbrains.jet.plugin.refactoring.changeSignature.JetParameterInfo;
import java.util.List;
import static org.jetbrains.jet.plugin.refactoring.changeSignature.ChangeSignaturePackage.runChangeSignature;
public class ChangeFunctionLiteralSignatureFix extends ChangeFunctionSignatureFix {
private final List<JetType> parameterTypes;
@@ -49,16 +54,18 @@ public class ChangeFunctionLiteralSignatureFix extends ChangeFunctionSignatureFi
}
@Override
protected void invoke(@NotNull Project project, Editor editor, JetFile file) {
JetFunctionPlatformDescriptorImpl platformDescriptor = new JetFunctionPlatformDescriptorImpl(functionDescriptor, element);
JetNameValidator validator = JetNameValidator.getCollectingValidator(project);
platformDescriptor.clearParameters();
for (JetType type : parameterTypes) {
String name = JetNameSuggester.suggestNames(type, validator, "param")[0];
platformDescriptor.addParameter(new JetParameterInfo(name, type));
}
showDialog(project, platformDescriptor);
protected void invoke(@NotNull final Project project, Editor editor, JetFile file) {
BindingContext bindingContext = AnalyzerFacadeWithCache.analyzeFileWithCache(file).getBindingContext();
runChangeSignature(project, functionDescriptor, new JetChangeSignatureConfiguration() {
@Override
public void configure(@NotNull JetChangeSignatureData changeSignatureData, @NotNull BindingContext bindingContext) {
JetNameValidator validator = JetNameValidator.getCollectingValidator(project);
changeSignatureData.clearParameters();
for (JetType type : parameterTypes) {
String name = JetNameSuggester.suggestNames(type, validator, "param")[0];
changeSignatureData.addParameter(new JetParameterInfo(name, type));
}
}
}, bindingContext, context, getText(), false);
}
}
@@ -16,9 +16,6 @@
package org.jetbrains.jet.plugin.quickfix;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiNameIdentifierOwner;
import com.intellij.psi.util.PsiTreeUtil;
@@ -41,12 +38,12 @@ import org.jetbrains.jet.plugin.JetBundle;
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
import org.jetbrains.jet.plugin.refactoring.JetNameSuggester;
import org.jetbrains.jet.plugin.refactoring.JetNameValidator;
import org.jetbrains.jet.plugin.refactoring.changeSignature.*;
import org.jetbrains.jet.plugin.refactoring.changeSignature.JetParameterInfo;
import java.util.List;
public abstract class ChangeFunctionSignatureFix extends JetIntentionAction<PsiElement> {
private final PsiElement context;
protected final PsiElement context;
protected final FunctionDescriptor functionDescriptor;
public ChangeFunctionSignatureFix(
@@ -70,21 +67,13 @@ public abstract class ChangeFunctionSignatureFix extends JetIntentionAction<PsiE
return false;
}
protected void showDialog(Project project, JetFunctionPlatformDescriptorImpl platformDescriptor) {
JetChangeSignatureDialog dialog = new JetChangeSignatureDialog(project, platformDescriptor, context, getText());
if (ApplicationManager.getApplication().isUnitTestMode())
performRefactoringSilently(dialog);
else
dialog.show();
}
protected static String getNewArgumentName(ValueArgument argument, JetNameValidator validator) {
JetValueArgumentName argumentName = argument.getArgumentName();
JetExpression expression = argument.getArgumentExpression();
if (argumentName != null)
if (argumentName != null) {
return validator.validateName(argumentName.getName());
}
else if (expression != null) {
return JetNameSuggester.suggestNames(expression, validator, "param")[0];
}
@@ -92,7 +81,11 @@ public abstract class ChangeFunctionSignatureFix extends JetIntentionAction<PsiE
return validator.validateName("param");
}
protected static JetParameterInfo getNewParameterInfo(BindingContext bindingContext, ValueArgument argument, JetNameValidator validator) {
protected static JetParameterInfo getNewParameterInfo(
BindingContext bindingContext,
ValueArgument argument,
JetNameValidator validator
) {
String name = getNewArgumentName(argument, validator);
JetExpression expression = argument.getArgumentExpression();
JetType type = expression != null ? bindingContext.get(BindingContext.EXPRESSION_TYPE, expression) : null;
@@ -100,32 +93,28 @@ public abstract class ChangeFunctionSignatureFix extends JetIntentionAction<PsiE
return new JetParameterInfo(name, type);
}
private static boolean hasTypeMismatches(List<ValueParameterDescriptor> parameters, List<? extends ValueArgument> arguments, BindingContext bindingContext) {
private static boolean hasTypeMismatches(
List<ValueParameterDescriptor> parameters,
List<? extends ValueArgument> arguments,
BindingContext bindingContext
) {
for (int i = 0; i < parameters.size(); i++) {
assert i < arguments.size(); // number of parameters must not be greater than the number of arguments (it's called only for TOO_MANY_ARGUMENTS error)
assert i <
arguments
.size(); // number of parameters must not be greater than the number of arguments (it's called only for TOO_MANY_ARGUMENTS error)
JetExpression argumentExpression = arguments.get(i).getArgumentExpression();
JetType argumentType = argumentExpression != null ? bindingContext.get(BindingContext.EXPRESSION_TYPE, argumentExpression) : null;
JetType argumentType =
argumentExpression != null ? bindingContext.get(BindingContext.EXPRESSION_TYPE, argumentExpression) : null;
JetType parameterType = parameters.get(i).getType();
if (argumentType == null || !JetTypeChecker.INSTANCE.isSubtypeOf(argumentType, parameterType))
if (argumentType == null || !JetTypeChecker.INSTANCE.isSubtypeOf(argumentType, parameterType)) {
return true;
}
}
return false;
}
protected void performRefactoringSilently(final JetChangeSignatureDialog dialog) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
JetChangeInfo changeInfo = dialog.evaluateChangeInfo();
JetChangeSignatureProcessor processor = new JetChangeSignatureProcessor(element.getProject(), changeInfo, getText());
processor.run();
Disposer.dispose(dialog.getDisposable());
}
});
}
public static JetSingleIntentionActionFactory createFactory() {
return new JetSingleIntentionActionFactory() {
@Override
@@ -134,8 +123,9 @@ public abstract class ChangeFunctionSignatureFix extends JetIntentionAction<PsiE
@SuppressWarnings("unchecked")
CallableDescriptor descriptor = ((DiagnosticWithParameters1<PsiElement, CallableDescriptor>) diagnostic).getA();
if (callElement != null)
if (callElement != null) {
return createFix(callElement, callElement, descriptor);
}
return null;
}
@@ -150,13 +140,17 @@ public abstract class ChangeFunctionSignatureFix extends JetIntentionAction<PsiE
DiagnosticWithParameters2<JetFunctionLiteral, Integer, List<JetType>> diagnosticWithParameters =
(DiagnosticWithParameters2<JetFunctionLiteral, Integer, List<JetType>>) diagnostic;
JetFunctionLiteral functionLiteral = diagnosticWithParameters.getPsiElement();
BindingContext bindingContext = AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) functionLiteral.getContainingFile()).getBindingContext();
BindingContext bindingContext =
AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) functionLiteral.getContainingFile()).getBindingContext();
DeclarationDescriptor descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, functionLiteral);
if (descriptor instanceof FunctionDescriptor)
return new ChangeFunctionLiteralSignatureFix(functionLiteral, (FunctionDescriptor) descriptor, diagnosticWithParameters.getB());
else
if (descriptor instanceof FunctionDescriptor) {
return new ChangeFunctionLiteralSignatureFix(functionLiteral, (FunctionDescriptor) descriptor,
diagnosticWithParameters.getB());
}
else {
return null;
}
}
};
}
@@ -168,10 +162,12 @@ public abstract class ChangeFunctionSignatureFix extends JetIntentionAction<PsiE
@SuppressWarnings("unchecked")
Object descriptor = ((DiagnosticWithParameters1<PsiNameIdentifierOwner, Object>) diagnostic).getA();
if (descriptor instanceof ValueParameterDescriptor)
if (descriptor instanceof ValueParameterDescriptor) {
return createFix(null, diagnostic.getPsiElement(), (CallableDescriptor) descriptor);
else
}
else {
return null;
}
}
};
}
@@ -180,31 +176,37 @@ public abstract class ChangeFunctionSignatureFix extends JetIntentionAction<PsiE
private static ChangeFunctionSignatureFix createFix(JetCallElement callElement, PsiElement context, CallableDescriptor descriptor) {
FunctionDescriptor functionDescriptor = null;
if (descriptor instanceof FunctionDescriptor)
if (descriptor instanceof FunctionDescriptor) {
functionDescriptor = (FunctionDescriptor) descriptor;
}
else if (descriptor instanceof ValueParameterDescriptor) {
DeclarationDescriptor containingDescriptor = descriptor.getContainingDeclaration();
if (containingDescriptor instanceof FunctionDescriptor)
if (containingDescriptor instanceof FunctionDescriptor) {
functionDescriptor = (FunctionDescriptor) containingDescriptor;
}
}
if (functionDescriptor != null) {
BindingContext bindingContext = AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) context.getContainingFile()).getBindingContext();
PsiElement declaration = BindingContextUtils.descriptorToDeclaration(bindingContext, functionDescriptor);
if (functionDescriptor == null) {
return null;
}
BindingContext bindingContext =
AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) context.getContainingFile()).getBindingContext();
PsiElement declaration = BindingContextUtils.descriptorToDeclaration(bindingContext, functionDescriptor);
if (declaration != null) {
if (descriptor instanceof ValueParameterDescriptor)
return new RemoveFunctionParametersFix(declaration, context, functionDescriptor, (ValueParameterDescriptor) descriptor);
else {
List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
List<? extends ValueArgument> arguments = callElement.getValueArguments();
if (declaration == null) {
return null;
}
if (descriptor instanceof ValueParameterDescriptor) {
return new RemoveFunctionParametersFix(declaration, context, functionDescriptor, (ValueParameterDescriptor) descriptor);
}
else {
List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
List<? extends ValueArgument> arguments = callElement.getValueArguments();
if (arguments.size() > parameters.size()) {
boolean hasTypeMismatches = hasTypeMismatches(parameters, arguments, bindingContext);
return new AddFunctionParametersFix(declaration, callElement, functionDescriptor, hasTypeMismatches);
}
}
if (arguments.size() > parameters.size()) {
boolean hasTypeMismatches = hasTypeMismatches(parameters, arguments, bindingContext);
return new AddFunctionParametersFix(declaration, callElement, functionDescriptor, hasTypeMismatches);
}
}
@@ -23,11 +23,16 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.plugin.JetBundle;
import org.jetbrains.jet.plugin.refactoring.changeSignature.JetFunctionPlatformDescriptorImpl;
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
import org.jetbrains.jet.plugin.refactoring.changeSignature.JetChangeSignatureConfiguration;
import org.jetbrains.jet.plugin.refactoring.changeSignature.JetChangeSignatureData;
import java.util.List;
import static org.jetbrains.jet.plugin.refactoring.changeSignature.ChangeSignaturePackage.runChangeSignature;
public class RemoveFunctionParametersFix extends ChangeFunctionSignatureFix {
private final ValueParameterDescriptor parameterToRemove;
@@ -49,9 +54,15 @@ public class RemoveFunctionParametersFix extends ChangeFunctionSignatureFix {
@Override
protected void invoke(@NotNull Project project, Editor editor, JetFile file) {
JetFunctionPlatformDescriptorImpl platformDescriptor = new JetFunctionPlatformDescriptorImpl(functionDescriptor, element);
List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
platformDescriptor.removeParameter(parameters.indexOf(parameterToRemove));
showDialog(project, platformDescriptor);
BindingContext bindingContext = AnalyzerFacadeWithCache.analyzeFileWithCache(file).getBindingContext();
runChangeSignature(project, functionDescriptor, new JetChangeSignatureConfiguration() {
@Override
public void configure(
@NotNull JetChangeSignatureData changeSignatureData, @NotNull BindingContext bindingContext
) {
List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
changeSignatureData.removeParameter(parameters.indexOf(parameterToRemove));
}
}, bindingContext, context, getText(), false);
}
}
@@ -32,12 +32,13 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.lexer.JetTokens;
import org.jetbrains.jet.plugin.JetLanguage;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JetChangeInfo implements ChangeInfo {
private final JetFunctionPlatformDescriptor oldDescriptor;
private final JetMethodDescriptor oldDescriptor;
private String newName;
private final JetType newReturnType;
private String newReturnTypeText;
@@ -49,7 +50,7 @@ public class JetChangeInfo implements ChangeInfo {
private Map<String, Integer> oldNameToParameterIndex;
public JetChangeInfo(
JetFunctionPlatformDescriptor oldDescriptor,
JetMethodDescriptor oldDescriptor,
String newName,
JetType newReturnType,
String newReturnTypeText,
@@ -215,7 +216,7 @@ public class JetChangeInfo implements ChangeInfo {
return oldDescriptor.getDescriptor();
}
public JetFunctionPlatformDescriptor getFunctionDescriptor() {
public JetMethodDescriptor getFunctionDescriptor() {
return oldDescriptor;
}
@@ -259,4 +260,9 @@ public class JetChangeInfo implements ChangeInfo {
public Language getLanguage() {
return JetLanguage.INSTANCE;
}
@NotNull
public Collection<PsiElement> getAffectedFunctions() {
return oldDescriptor.getAffectedFunctions();
}
}
@@ -0,0 +1,206 @@
/*
* Copyright 2010-2013 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.jet.plugin.refactoring.changeSignature
import com.intellij.ide.IdeBundle
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.util.Disposer
import com.intellij.psi.PsiElement
import org.jetbrains.jet.lang.descriptors.*
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.lang.resolve.OverridingUtil
import org.jetbrains.jet.plugin.JetBundle
import org.jetbrains.jet.renderer.DescriptorRenderer
import java.util.*
import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor.Kind.*
import org.jetbrains.jet.lang.resolve.BindingContextUtils.*
import org.jetbrains.annotations.TestOnly
public trait JetChangeSignatureConfiguration {
fun configure(changeSignatureData: JetChangeSignatureData, bindingContext: BindingContext)
}
public fun runChangeSignature(project: Project,
functionDescriptor: FunctionDescriptor,
configuration: JetChangeSignatureConfiguration,
bindingContext: BindingContext,
defaultValueContext: PsiElement,
commandName: String? = null,
performSilently: Boolean = false): Unit {
JetChangeSignature(project, functionDescriptor, configuration, bindingContext, defaultValueContext, commandName, performSilently).run()
}
public class JetChangeSignature(val project: Project,
val functionDescriptor: FunctionDescriptor,
val configuration: JetChangeSignatureConfiguration,
val bindingContext: BindingContext,
val defaultValueContext: PsiElement,
val commandName: String?,
val performSilently: Boolean) {
private val LOG = Logger.getInstance(javaClass<JetChangeSignature>())
public fun run() {
if (functionDescriptor.getKind() == SYNTHESIZED) {
LOG.error("Change signature refactoring should not be called for synthesized member " + functionDescriptor)
return
}
val closestModifiableDescriptors = getClosestModifiableDescriptors()
assert(!closestModifiableDescriptors.isEmpty()) { "Should contain functionDescriptor itself or some of its super declarations" }
val deepestSuperDeclarations = getDeepestSuperDeclarations()
if (ApplicationManager.getApplication()!!.isUnitTestMode()) {
showChangeSignatureDialog(deepestSuperDeclarations)
return
}
if ((closestModifiableDescriptors.size()) == 1 && deepestSuperDeclarations.equals(closestModifiableDescriptors)) {
showChangeSignatureDialog(closestModifiableDescriptors)
return
}
val isSingleFunctionSelected = closestModifiableDescriptors.size() == 1
val selectedFunction = if (isSingleFunctionSelected) closestModifiableDescriptors.first() else functionDescriptor
val optionsForDialog = buildDialogOptions(isSingleFunctionSelected)
val code = showSuperFunctionWarningDialog(deepestSuperDeclarations, selectedFunction, optionsForDialog.copyToArray())
when {
performForWholeHierarchy(optionsForDialog, code) -> {
showChangeSignatureDialog(deepestSuperDeclarations)
}
performForSelectedFunctionOnly(optionsForDialog, code) -> {
showChangeSignatureDialog(closestModifiableDescriptors)
}
else -> {
//do nothing
}
}
}
private fun getClosestModifiableDescriptors(): Set<FunctionDescriptor> {
val kind = functionDescriptor.getKind()
if (kind == DELEGATION || kind == FAKE_OVERRIDE) {
return getDirectlyOverriddenDeclarations(functionDescriptor)
}
assert(kind == DECLARATION) { "Unexpected callable kind: " + kind }
return Collections.singleton(functionDescriptor)
}
fun getDeepestSuperDeclarations(): Set<FunctionDescriptor> {
val overriddenDeclarations = getAllOverriddenDeclarations(functionDescriptor)
if (overriddenDeclarations.isEmpty()) {
return Collections.singleton(functionDescriptor)
}
return OverridingUtil.filterOutOverriding(overriddenDeclarations)
}
private fun showChangeSignatureDialog(descriptorsForSignatureChange: Collection<FunctionDescriptor>) {
val dialog = createChangeSignatureDialog(descriptorsForSignatureChange)
if (dialog == null) {
return
}
if (performSilently || ApplicationManager.getApplication()!!.isUnitTestMode()) {
performRefactoringSilently(dialog)
}
else {
dialog.show()
}
}
fun createChangeSignatureDialog(descriptorsForSignatureChange: Collection<FunctionDescriptor>): JetChangeSignatureDialog? {
val baseDescriptor = preferContainedInClass(descriptorsForSignatureChange)
val functionDeclaration = callableDescriptorToDeclaration(bindingContext, baseDescriptor)
if (functionDeclaration == null) {
LOG.error("Could not find declaration for " + baseDescriptor)
return null
}
val changeSignatureData = JetChangeSignatureData(baseDescriptor, functionDeclaration, bindingContext, descriptorsForSignatureChange)
configuration.configure(changeSignatureData, bindingContext)
return JetChangeSignatureDialog(project, changeSignatureData, defaultValueContext, commandName)
}
private fun performRefactoringSilently(dialog: JetChangeSignatureDialog) {
ApplicationManager.getApplication()!!.runWriteAction {
dialog.createRefactoringProcessor().run()
Disposer.dispose(dialog.getDisposable()!!)
}
}
private fun preferContainedInClass(descriptorsForSignatureChange: Collection<FunctionDescriptor>): FunctionDescriptor {
for (descriptor in descriptorsForSignatureChange) {
val containingDeclaration = descriptor.getContainingDeclaration()
if (containingDeclaration is ClassDescriptor && containingDeclaration.getKind() != ClassKind.TRAIT) {
return descriptor
}
}
//choose at random
return descriptorsForSignatureChange.first()
}
private fun buildDialogOptions(isSingleFunctionSelected: Boolean): List<String> {
val optionsForDialog = ArrayList<String>()
optionsForDialog.add(if (isSingleFunctionSelected) Messages.YES_BUTTON else Messages.OK_BUTTON)
if (isSingleFunctionSelected) {
optionsForDialog.add(Messages.NO_BUTTON)
}
optionsForDialog.add(Messages.CANCEL_BUTTON)
return optionsForDialog
}
private fun performForWholeHierarchy(optionsForDialog: List<String>, code: Int): Boolean {
return buttonPressed(code, optionsForDialog, Messages.YES_BUTTON) || buttonPressed(code, optionsForDialog, Messages.OK_BUTTON)
}
private fun performForSelectedFunctionOnly(dialogButtons: List<String>, code: Int): Boolean {
return buttonPressed(code, dialogButtons, Messages.NO_BUTTON)
}
private fun buttonPressed(code: Int, dialogButtons: List<String>, button: String): Boolean {
return code == dialogButtons.indexOf(button) && dialogButtons.contains(button)
}
private fun showSuperFunctionWarningDialog(superFunctions: Collection<FunctionDescriptor>,
functionFromEditor: FunctionDescriptor,
options: Array<String>): Int {
val superString = superFunctions.map {
it.getContainingDeclaration().getName().asString()
}.makeString(prefix = "\n ", separator = ",\n ", postfix = ".\n\n")
val message = JetBundle.message("x.overrides.y.in.class.list",
DescriptorRenderer.COMPACT.render(functionFromEditor),
functionFromEditor.getContainingDeclaration().getName().asString(), superString,
"refactor")
val title = IdeBundle.message("title.warning")
val icon = Messages.getQuestionIcon()
return Messages.showDialog(message, title!!, options, 0, icon!!)
}
}
TestOnly public fun getChangeSignatureDialog(project: Project,
functionDescriptor: FunctionDescriptor,
configuration: JetChangeSignatureConfiguration,
bindingContext: BindingContext,
defaultValueContext: PsiElement): JetChangeSignatureDialog? {
val jetChangeSignature = JetChangeSignature(project, functionDescriptor, configuration, bindingContext, defaultValueContext, null, true)
return jetChangeSignature.createChangeSignatureDialog(jetChangeSignature.getDeepestSuperDeclarations())
}
@@ -0,0 +1,213 @@
/*
* Copyright 2010-2013 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.jet.plugin.refactoring.changeSignature;
import com.google.common.collect.Sets;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.refactoring.changeSignature.MethodDescriptor;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.asJava.LightClassUtil;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.impl.AnonymousFunctionDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.java.jetAsJava.JetClsMethod;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.renderer.DescriptorRenderer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
public final class JetChangeSignatureData implements JetMethodDescriptor {
@NotNull
private final FunctionDescriptor baseDescriptor;
@NotNull
private final PsiElement baseDeclaration;
@NotNull
private final List<JetParameterInfo> parameters;
@NotNull
private final BindingContext bindingContext;
@NotNull
private final Collection<FunctionDescriptor> descriptorsForSignatureChange;
public JetChangeSignatureData(
@NotNull FunctionDescriptor baseDescriptor,
@NotNull PsiElement baseDeclaration,
@NotNull BindingContext bindingContext,
@NotNull Collection<FunctionDescriptor> descriptorsForSignatureChange
) {
this.baseDescriptor = baseDescriptor;
this.baseDeclaration = baseDeclaration;
this.bindingContext = bindingContext;
this.descriptorsForSignatureChange = descriptorsForSignatureChange;
final List<JetParameter> valueParameters = this.baseDeclaration instanceof JetFunction
? ((JetFunction) this.baseDeclaration).getValueParameters()
: ((JetClass) this.baseDeclaration).getPrimaryConstructorParameters();
this.parameters = new ArrayList<JetParameterInfo>(
ContainerUtil.map(this.baseDescriptor.getValueParameters(), new Function<ValueParameterDescriptor, JetParameterInfo>() {
@Override
public JetParameterInfo fun(ValueParameterDescriptor param) {
JetParameter parameter = valueParameters.get(param.getIndex());
return new JetParameterInfo(param.getIndex(), param.getName().asString(), param.getType(),
parameter.getDefaultValue(), parameter.getValOrVarNode());
}
}));
}
@Override
@NotNull
public List<JetParameterInfo> getParameters() {
return parameters;
}
public void addParameter(JetParameterInfo parameter) {
parameters.add(parameter);
}
public void removeParameter(int index) {
parameters.remove(index);
}
public void clearParameters() {
parameters.clear();
}
@Override
@NotNull
public Collection<PsiElement> getAffectedFunctions() {
Set<PsiElement> result = Sets.newHashSet();
for (FunctionDescriptor descriptor : descriptorsForSignatureChange) {
result.addAll(computeHierarchyFrom(descriptor));
}
return result;
}
@NotNull
private Collection<PsiElement> computeHierarchyFrom(@NotNull FunctionDescriptor baseDescriptor) {
PsiElement declaration = BindingContextUtils.callableDescriptorToDeclaration(bindingContext, baseDescriptor);
Set<PsiElement> result = Sets.newHashSet();
result.add(declaration);
if (!(declaration instanceof JetNamedFunction)) {
return result;
}
PsiMethod lightMethod = LightClassUtil.getLightClassMethod((JetNamedFunction) declaration);
// there are valid situations when light method is null: local functions and literals
if (lightMethod == null) {
return result;
}
Collection<PsiMethod> overridingMethods = OverridingMethodsSearch.search(lightMethod).findAll();
List<PsiMethod> jetLightMethods = ContainerUtil.filter(overridingMethods, new Condition<PsiMethod>() {
@Override
public boolean value(PsiMethod method) {
return method instanceof JetClsMethod;
}
});
List<JetDeclaration> jetFunctions = ContainerUtil.map(jetLightMethods, new Function<PsiMethod, JetDeclaration>() {
@Override
public JetDeclaration fun(PsiMethod method) {
return ((JetClsMethod) method).getOrigin();
}
});
result.addAll(jetFunctions);
return result;
}
@Override
public String getName() {
if (baseDescriptor instanceof ConstructorDescriptor) {
return baseDescriptor.getContainingDeclaration().getName().asString();
}
else if (baseDescriptor instanceof AnonymousFunctionDescriptor) {
return "";
}
else {
return baseDescriptor.getName().asString();
}
}
@Override
public int getParametersCount() {
return baseDescriptor.getValueParameters().size();
}
@Override
public Visibility getVisibility() {
return baseDescriptor.getVisibility();
}
@Override
public PsiElement getMethod() {
return baseDeclaration;
}
@Override
public boolean canChangeVisibility() {
DeclarationDescriptor parent = baseDescriptor.getContainingDeclaration();
return !(baseDescriptor instanceof AnonymousFunctionDescriptor ||
parent instanceof ClassDescriptor && ((ClassDescriptor) parent).getKind() == ClassKind.TRAIT);
}
@Override
public boolean canChangeParameters() {
return true;
}
@Override
public boolean canChangeName() {
return !(baseDescriptor instanceof ConstructorDescriptor ||
baseDescriptor instanceof AnonymousFunctionDescriptor);
}
@Override
public MethodDescriptor.ReadWriteOption canChangeReturnType() {
return baseDescriptor instanceof ConstructorDescriptor ? ReadWriteOption.None : ReadWriteOption.ReadWrite;
}
@Override
public boolean isConstructor() {
return baseDescriptor instanceof ConstructorDescriptor;
}
@NotNull
@Override
public PsiElement getContext() {
return baseDeclaration;
}
@Nullable
@Override
public FunctionDescriptor getDescriptor() {
return baseDescriptor;
}
@Override
@Nullable
public String getReturnTypeText() {
JetType returnType = baseDescriptor.getReturnType();
return returnType != null ? DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(returnType) : null;
}
}
@@ -71,7 +71,7 @@ public class JetChangeSignatureDialog extends ChangeSignatureDialogBase<
JetParameterInfo,
PsiElement,
Visibility,
JetFunctionPlatformDescriptor,
JetMethodDescriptor,
ParameterTableModelItemBase<JetParameterInfo>,
JetFunctionParameterTableModel
>
@@ -79,12 +79,8 @@ public class JetChangeSignatureDialog extends ChangeSignatureDialogBase<
private final JetGeneratedInfo generatedInfo = new JetGeneratedInfo();
private final String commandName;
public JetChangeSignatureDialog(Project project, JetFunctionPlatformDescriptor descriptor, PsiElement context) {
this(project, descriptor, context, null);
}
public JetChangeSignatureDialog(Project project, JetFunctionPlatformDescriptor descriptor, PsiElement context, String commandName) {
super(project, descriptor, false, context);
public JetChangeSignatureDialog(Project project, @NotNull JetMethodDescriptor methodDescriptor, PsiElement context, String commandName) {
super(project, methodDescriptor, false, context);
this.commandName = commandName;
}
@@ -94,7 +90,7 @@ public class JetChangeSignatureDialog extends ChangeSignatureDialogBase<
}
@Override
protected JetFunctionParameterTableModel createParametersInfoModel(JetFunctionPlatformDescriptor descriptor) {
protected JetFunctionParameterTableModel createParametersInfoModel(JetMethodDescriptor descriptor) {
if (descriptor.isConstructor())
return new JetConstructorParameterTableModel(myDefaultValueContext);
else
@@ -399,6 +395,7 @@ public class JetChangeSignatureDialog extends ChangeSignatureDialogBase<
}
@Override
@NotNull
protected BaseRefactoringProcessor createRefactoringProcessor() {
return new JetChangeSignatureProcessor(myProject, evaluateChangeInfo(), commandName != null ? commandName : getTitle());
}
@@ -414,6 +411,7 @@ public class JetChangeSignatureDialog extends ChangeSignatureDialogBase<
String returnTypeText = myReturnTypeCodeFragment != null ? myReturnTypeCodeFragment.getText().trim() : "";
return new JetChangeInfo(myMethod, getMethodName(), getReturnType(), returnTypeText,
getVisibility(), parameters, myDefaultValueContext, generatedInfo);
getVisibility(), parameters, myDefaultValueContext, generatedInfo
);
}
}
@@ -31,27 +31,21 @@ import com.intellij.refactoring.changeSignature.ChangeSignatureHandler;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.impl.FunctionDescriptorImpl;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
import org.jetbrains.jet.plugin.refactoring.JetRefactoringBundle;
import static org.jetbrains.jet.plugin.refactoring.changeSignature.ChangeSignaturePackage.runChangeSignature;
public class JetChangeSignatureHandler implements ChangeSignatureHandler {
@Nullable
@Override
public PsiElement findTargetMember(PsiFile file, Editor editor) {
return findTargetMember(file.findElementAt(editor.getCaretModel().getOffset()));
}
@Nullable
@Override
public PsiElement findTargetMember(PsiElement element) {
public static PsiElement findTargetForRefactoring(@NotNull PsiElement element) {
if (PsiTreeUtil.getParentOfType(element, JetParameterList.class) != null) {
return PsiTreeUtil.getParentOfType(element, JetFunction.class, JetClass.class);
}
@@ -62,35 +56,77 @@ public class JetChangeSignatureHandler implements ChangeSignatureHandler {
}
PsiElement elementParent = element.getParent();
if (elementParent instanceof JetNamedFunction && ((JetNamedFunction)elementParent).getNameIdentifier()==element) {
if (elementParent instanceof JetNamedFunction && ((JetNamedFunction) elementParent).getNameIdentifier() == element) {
return elementParent;
}
if (elementParent instanceof JetClass && ((JetClass)elementParent).getNameIdentifier()==element) {
if (elementParent instanceof JetClass && ((JetClass) elementParent).getNameIdentifier() == element) {
return elementParent;
}
JetCallElement call = PsiTreeUtil.getParentOfType(element, JetCallExpression.class, JetDelegatorToSuperCall.class);
if (call != null) {
JetExpression receiverExpr = call instanceof JetCallExpression ? call.getCalleeExpression() :
((JetDelegatorToSuperCall)call).getCalleeExpression().getConstructorReferenceExpression();
if (call == null) {
return null;
}
JetExpression receiverExpr = call instanceof JetCallExpression ? call.getCalleeExpression() :
((JetDelegatorToSuperCall) call).getCalleeExpression().getConstructorReferenceExpression();
if (receiverExpr instanceof JetSimpleNameExpression) {
BindingContext bindingContext = AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) element.getContainingFile()).getBindingContext();
DeclarationDescriptor descriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) receiverExpr);
if (receiverExpr instanceof JetSimpleNameExpression) {
BindingContext bindingContext =
AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) element.getContainingFile()).getBindingContext();
DeclarationDescriptor descriptor =
bindingContext.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) receiverExpr);
if (descriptor != null) {
PsiElement declaration = BindingContextUtils.descriptorToDeclaration(bindingContext, descriptor);
if (declaration instanceof JetNamedFunction || declaration instanceof JetClass) {
return declaration;
}
}
if (descriptor instanceof ClassDescriptor || descriptor instanceof FunctionDescriptor) {
return receiverExpr;
}
}
return null;
}
public static void invokeChangeSignature(
@NotNull PsiElement element,
@NotNull PsiElement context,
@NotNull Project project,
@Nullable Editor editor
) {
BindingContext bindingContext =
AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) element.getContainingFile()).getBindingContext();
FunctionDescriptor functionDescriptor = findDescriptor(element, project, editor, bindingContext);
if (functionDescriptor == null) {
return;
}
runChangeSignature(project, functionDescriptor, emptyConfiguration(), bindingContext, context, null, false);
}
@TestOnly
public static JetChangeSignatureConfiguration getConfiguration() {
return emptyConfiguration();
}
private static JetChangeSignatureConfiguration emptyConfiguration() {
return new JetChangeSignatureConfiguration() {
@Override
public void configure(
@NotNull JetChangeSignatureData changeSignatureData, @NotNull BindingContext bindingContext
) {
//do nothing
}
};
}
@Nullable
@Override
public PsiElement findTargetMember(PsiFile file, Editor editor) {
return findTargetMember(file.findElementAt(editor.getCaretModel().getOffset()));
}
@Nullable
@Override
public PsiElement findTargetMember(PsiElement element) {
return findTargetForRefactoring(element);
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) {
editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
@@ -99,12 +135,9 @@ public class JetChangeSignatureHandler implements ChangeSignatureHandler {
element = LangDataKeys.PSI_ELEMENT.getData(dataContext);
}
if (element != null) {
JetChangeSignatureDialog dialog = createDialog(element, file.findElementAt(editor.getCaretModel().getOffset()), project, editor);
if (dialog != null) {
dialog.show();
}
PsiElement elementAtCaret = file.findElementAt(editor.getCaretModel().getOffset());
if (element != null && elementAtCaret != null) {
invokeChangeSignature(element, elementAtCaret, project, editor);
}
}
@@ -112,32 +145,45 @@ public class JetChangeSignatureHandler implements ChangeSignatureHandler {
public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, @Nullable DataContext dataContext) {
if (elements.length != 1) return;
Editor editor = dataContext != null ? PlatformDataKeys.EDITOR.getData(dataContext) : null;
JetChangeSignatureDialog dialog = createDialog(elements[0], elements[0], project, editor);
if (dialog != null) {
dialog.show();
}
invokeChangeSignature(elements[0], elements[0], project, editor);
}
@Nullable
public static JetChangeSignatureDialog createDialog(@NotNull PsiElement element, PsiElement context, Project project, Editor editor) {
if (!CommonRefactoringUtil.checkReadOnlyStatus(project, element)) return null;
BindingContext bindingContext = AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile)element.getContainingFile()).getBindingContext();
DeclarationDescriptor descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element);
@Override
public String getTargetNotFoundMessage() {
return JetRefactoringBundle.message("error.wrong.caret.position.function.or.constructor.name");
}
@Nullable
public static FunctionDescriptor findDescriptor(
@NotNull PsiElement element,
@NotNull Project project,
@Nullable Editor editor,
BindingContext bindingContext
) {
if (!CommonRefactoringUtil.checkReadOnlyStatus(project, element)) return null;
DeclarationDescriptor descriptor;
if (element instanceof JetSimpleNameExpression) {
descriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, (JetReferenceExpression) element);
} else {
descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element);
}
if (descriptor instanceof ClassDescriptor) {
descriptor = ((ClassDescriptor) descriptor).getUnsubstitutedPrimaryConstructor();
}
if (descriptor instanceof FunctionDescriptorImpl) {
if (descriptor instanceof FunctionDescriptor) {
for (ValueParameterDescriptor parameter : ((FunctionDescriptor) descriptor).getValueParameters()) {
if (parameter.getVarargElementType() != null) {
String message = JetRefactoringBundle.message("error.cant.refactor.vararg.functions");
CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.CHANGE_SIGNATURE);
CommonRefactoringUtil.showErrorHint(project, editor, message,
REFACTORING_NAME,
HelpID.CHANGE_SIGNATURE);
return null;
}
}
return new JetChangeSignatureDialog(project, new JetFunctionPlatformDescriptorImpl((FunctionDescriptor) descriptor, element), context);
return (FunctionDescriptor) descriptor;
}
else {
String message = RefactoringBundle.getCannotRefactorMessage(JetRefactoringBundle.message(
@@ -146,10 +192,4 @@ public class JetChangeSignatureHandler implements ChangeSignatureHandler {
return null;
}
}
@Nullable
@Override
public String getTargetNotFoundMessage() {
return JetRefactoringBundle.message("error.wrong.caret.position.function.or.constructor.name");
}
}
@@ -66,12 +66,9 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
}
private static void findAllMethodUsages(JetChangeInfo changeInfo, Set<UsageInfo> result) {
PsiElement method = changeInfo.getMethod();
if (method != null)
findOneMethodUsages(method, changeInfo, result, false);
//TODO overridden methods, tests for different parameter names in overridden methods
for (PsiElement affectedFunction : changeInfo.getAffectedFunctions()) {
findOneMethodUsages(affectedFunction, changeInfo, result, false);
}
}
private static void findOneMethodUsages(@NotNull PsiElement functionPsi, JetChangeInfo changeInfo,
@@ -1,140 +0,0 @@
/*
* Copyright 2010-2013 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.jet.plugin.refactoring.changeSignature;
import com.intellij.psi.PsiElement;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.impl.AnonymousFunctionDescriptor;
import org.jetbrains.jet.lang.psi.JetClass;
import org.jetbrains.jet.lang.psi.JetFunction;
import org.jetbrains.jet.lang.psi.JetParameter;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.renderer.DescriptorRenderer;
import java.util.ArrayList;
import java.util.List;
public class JetFunctionPlatformDescriptorImpl implements JetFunctionPlatformDescriptor {
private final FunctionDescriptor funDescriptor;
private final PsiElement funElement;
private final List<JetParameterInfo> parameters;
public JetFunctionPlatformDescriptorImpl(FunctionDescriptor descriptor, PsiElement element) {
funDescriptor = descriptor;
funElement = element;
final List<JetParameter> valueParameters = funElement instanceof JetFunction
? ((JetFunction) funElement).getValueParameters()
: ((JetClass) funElement).getPrimaryConstructorParameters();
parameters = new ArrayList<JetParameterInfo>(ContainerUtil.map(funDescriptor.getValueParameters(), new Function<ValueParameterDescriptor, JetParameterInfo>() {
@Override
public JetParameterInfo fun(ValueParameterDescriptor param) {
JetParameter parameter = valueParameters.get(param.getIndex());
return new JetParameterInfo(param.getIndex(), param.getName().asString(), param.getType(), parameter.getDefaultValue(), parameter.getValOrVarNode());
}
}));
}
@Override
public String getName() {
if (funDescriptor instanceof ConstructorDescriptor)
return funDescriptor.getContainingDeclaration().getName().asString();
else if (funDescriptor instanceof AnonymousFunctionDescriptor)
return "";
else
return funDescriptor.getName().asString();
}
@Override
public List<JetParameterInfo> getParameters() {
return parameters;
}
public void addParameter(JetParameterInfo parameter) {
parameters.add(parameter);
}
public void removeParameter(int index) {
parameters.remove(index);
}
public void clearParameters() {
parameters.clear();
}
@Override
public int getParametersCount() {
return funDescriptor.getValueParameters().size();
}
@Override
public Visibility getVisibility() {
return funDescriptor.getVisibility();
}
@Override
public PsiElement getMethod() {
return funElement;
}
@NotNull
@Override
public PsiElement getContext() {
return funElement;
}
@Override
public boolean isConstructor() {
return funDescriptor instanceof ConstructorDescriptor;
}
@Override
public boolean canChangeVisibility() {
DeclarationDescriptor parent = funDescriptor.getContainingDeclaration();
return !(funDescriptor instanceof AnonymousFunctionDescriptor || parent instanceof ClassDescriptor && ((ClassDescriptor) parent).getKind() == ClassKind.TRAIT);
}
@Override
public boolean canChangeParameters() {
return true;
}
@Override
public boolean canChangeName() {
return !(funDescriptor instanceof ConstructorDescriptor || funDescriptor instanceof AnonymousFunctionDescriptor);
}
@Override
public ReadWriteOption canChangeReturnType() {
return isConstructor() ? ReadWriteOption.None : ReadWriteOption.ReadWrite;
}
@Override
public FunctionDescriptor getDescriptor() {
return funDescriptor;
}
@Override
@Nullable
public String getReturnTypeText() {
JetType returnType = funDescriptor.getReturnType();
return returnType != null ? DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(returnType) : null;
}
}
@@ -23,7 +23,12 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.Visibility;
public interface JetFunctionPlatformDescriptor extends MethodDescriptor<JetParameterInfo, Visibility> {
import java.util.Collection;
public interface JetMethodDescriptor extends MethodDescriptor<JetParameterInfo, Visibility> {
@NotNull
Collection<PsiElement> getAffectedFunctions();
boolean isConstructor();
@Nullable
@@ -76,7 +76,7 @@ public class JetParameterInfo implements ParameterInfo {
return name;
}
public String getInheritedName(boolean isInherited, @Nullable PsiElement inheritedFunction, @NotNull JetFunctionPlatformDescriptor baseFunction) {
public String getInheritedName(boolean isInherited, @Nullable PsiElement inheritedFunction, @NotNull JetMethodDescriptor baseFunction) {
if (!(inheritedFunction instanceof JetFunction))
return name;
@@ -165,7 +165,7 @@ public class JetParameterInfo implements ParameterInfo {
this.valOrVar = valOrVar;
}
public String getDeclarationSignature(boolean isInherited, PsiElement inheritedFunction, JetFunctionPlatformDescriptor baseFunction) {
public String getDeclarationSignature(boolean isInherited, PsiElement inheritedFunction, JetMethodDescriptor baseFunction) {
StringBuilder buffer = new StringBuilder();
JetValVar valVar = getValOrVar();
@@ -0,0 +1,65 @@
// "Add parameter to function 'f'" "true"
trait OA {
fun f(a: Int,
i: Int)
}
trait OB {
fun f(a: Int,
i: Int)
}
trait O : OA, OB {
override fun f(a: Int,
i: Int)
}
trait OO : O {
override fun f(a: Int,
i: Int) {
}
}
trait OOO : OO {
override fun f(a: Int,
i: Int) {}
}
trait OOOA : OOO {
override fun f(a: Int,
i: Int) {
}
}
trait OOOB : OOO {
override fun f(a: Int,
i: Int) {
}
}
fun usage(o: OA) {
o.f(1, 12)
}
fun usage(o: OB) {
o.f(1, 12)
}
fun usage(o: O) {
o.f(1, 12)
}
fun usage(o: OO) {
o.f(13, 12)
}
fun usage(o: OOO) {
o.f(3, 12)
}
fun usage(o: OOOA) {
o.f(3, 12)
}
fun usage(o: OOOB) {
o.f(3, 12)
}
@@ -0,0 +1,58 @@
// "Remove parameter 'a'" "true"
trait OA {
fun f()
}
trait OB {
fun f(a: Int)
}
trait O : OA, OB {
override fun f()
}
trait OO : O {
override fun f() {
}
}
trait OOO : OO {
override fun f() {}
}
trait OOOA : OOO {
override fun f() {
}
}
trait OOOB : OOO {
override fun f() {
}
}
fun usage(o: OA) {
o.f()
}
fun usage(o: OB) {
o.f(1)
}
fun usage(o: O) {
o.f()
}
fun usage(o: OO) {
o.f()
}
fun usage(o: OOO) {
o.f()
}
fun usage(o: OOOA) {
o.f()
}
fun usage(o: OOOB) {
o.f()
}
@@ -0,0 +1,65 @@
// "Add parameter to function 'f'" "true"
trait OA {
fun f(a: Int,
s: String)
}
trait OB {
fun f(a: Int,
s: String)
}
trait O : OA, OB {
override fun f(a: Int,
s: String)
}
trait OO : O {
override fun f(a: Int,
s: String) {
}
}
trait OOO : OO {
override fun f(a: Int,
s: String) {}
}
trait OOOA : OOO {
override fun f(a: Int,
s: String) {
}
}
trait OOOB : OOO {
override fun f(a: Int,
s: String) {
}
}
fun usage(o: OA) {
o.f(1, "asdv")
}
fun usage(o: OB) {
o.f(1, "asdv")
}
fun usage(o: O) {
o.f(1, "asdv")
}
fun usage(o: OO) {
o.f(13, "asdv")
}
fun usage(o: OOO) {
o.f(3, "asdv")
}
fun usage(o: OOOA) {
o.f(3, "asdv")
}
fun usage(o: OOOB) {
o.f(3, "asdv")
}
@@ -0,0 +1,28 @@
// "Add parameter to function 'f'" "true"
trait O {
fun f(a: Int,
i: Int)
}
trait OO : O {
override fun f(a: Int,
i: Int) {
}
}
trait OOO : OO {
override fun f(a: Int,
i: Int) {}
}
fun usage(o: O) {
o.f(1, 12)
}
fun usage(o: OO) {
o.f(13, 12)
}
fun usage(o: OOO) {
o.f(3, 12)
}
@@ -0,0 +1,58 @@
// "Add parameter to function 'f'" "true"
trait OA {
fun f(a: Int)
}
trait OB {
fun f(a: Int)
}
trait O : OA, OB {
override fun f(a: Int)
}
trait OO : O {
override fun f(a: Int) {
}
}
trait OOO : OO {
override fun f(a: Int) {}
}
trait OOOA : OOO {
override fun f(a: Int) {
}
}
trait OOOB : OOO {
override fun f(a: Int) {
}
}
fun usage(o: OA) {
o.f(1)
}
fun usage(o: OB) {
o.f(1)
}
fun usage(o: O) {
o.f(1)
}
fun usage(o: OO) {
o.f(13, <caret>12)
}
fun usage(o: OOO) {
o.f(3)
}
fun usage(o: OOOA) {
o.f(3)
}
fun usage(o: OOOB) {
o.f(3)
}
@@ -0,0 +1,58 @@
// "Remove parameter 'a'" "true"
trait OA {
fun f(a: Int)
}
trait OB {
fun f(a: Int)
}
trait O : OA, OB {
override fun f(a: Int)
}
trait OO : O {
override fun f(a: Int) {
}
}
trait OOO : OO {
override fun f(a: Int) {}
}
trait OOOA : OOO {
override fun f(a: Int) {
}
}
trait OOOB : OOO {
override fun f(a: Int) {
}
}
fun usage(o: OA) {
o.f(<caret>)
}
fun usage(o: OB) {
o.f(1)
}
fun usage(o: O) {
o.f(1)
}
fun usage(o: OO) {
o.f(13)
}
fun usage(o: OOO) {
o.f(3)
}
fun usage(o: OOOA) {
o.f(3)
}
fun usage(o: OOOB) {
o.f(3)
}
@@ -0,0 +1,58 @@
// "Add parameter to function 'f'" "true"
trait OA {
fun f(a: Int)
}
trait OB {
fun f(a: Int)
}
trait O : OA, OB {
override fun f(a: Int)
}
trait OO : O {
override fun f(a: Int) {
}
}
trait OOO : OO {
override fun f(a: Int) {}
}
trait OOOA : OOO {
override fun f(a: Int) {
}
}
trait OOOB : OOO {
override fun f(a: Int) {
}
}
fun usage(o: OA) {
o.f(1)
}
fun usage(o: OB) {
o.f(1)
}
fun usage(o: O) {
o.f(1)
}
fun usage(o: OO) {
o.f(13)
}
fun usage(o: OOO) {
o.f(3)
}
fun usage(o: OOOA) {
o.f(3)
}
fun usage(o: OOOB) {
o.f(3, <caret>"asdv")
}
@@ -0,0 +1,25 @@
// "Add parameter to function 'f'" "true"
trait O {
fun f(a: Int)
}
trait OO : O {
override fun f(a: Int) {
}
}
trait OOO : OO {
override fun f(a: Int) {}
}
fun usage(o: O) {
o.f(1)
}
fun usage(o: OO) {
o.f(13, <caret>12)
}
fun usage(o: OOO) {
o.f(3)
}
@@ -0,0 +1,15 @@
trait A {
fun f(i: Int) {}
}
trait B {
fun f(i: Int) {}
}
trait C : A, B {
}
fun usage(c: C) {
c.f()
}
@@ -0,0 +1,15 @@
trait A {
fun f() {}
}
trait B {
fun f() {}
}
trait C : A, B {
}
fun usage(c: C) {
c.<caret>f()
}
@@ -0,0 +1,11 @@
class A {
fun f(param: Int)
}
trait B {
fun f(p: Int)
}
class C : A, B {
override fun <caret>f(p: Int) {}
}
@@ -389,6 +389,26 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
doTest("idea/testData/quickfix/changeSignature/beforeChangeFunctionLiteralParameters2.kt");
}
@TestMetadata("beforeComplexHierarchy.kt")
public void testComplexHierarchy() throws Exception {
doTest("idea/testData/quickfix/changeSignature/beforeComplexHierarchy.kt");
}
@TestMetadata("beforeComplexHierarchyHead.kt")
public void testComplexHierarchyHead() throws Exception {
doTest("idea/testData/quickfix/changeSignature/beforeComplexHierarchyHead.kt");
}
@TestMetadata("beforeComplexHierarchyTail.kt")
public void testComplexHierarchyTail() throws Exception {
doTest("idea/testData/quickfix/changeSignature/beforeComplexHierarchyTail.kt");
}
@TestMetadata("beforeLinearHierarchy.kt")
public void testLinearHierarchy() throws Exception {
doTest("idea/testData/quickfix/changeSignature/beforeLinearHierarchy.kt");
}
@TestMetadata("beforeRemoveConstructorParameter.kt")
public void testRemoveConstructorParameter() throws Exception {
doTest("idea/testData/quickfix/changeSignature/beforeRemoveConstructorParameter.kt");
@@ -17,6 +17,7 @@
package org.jetbrains.jet.plugin.refactoring.changeSignature;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
@@ -25,16 +26,22 @@ import com.intellij.refactoring.BaseRefactoringProcessor;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.testFramework.LightCodeInsightTestCase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.Visibilities;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetPsiFactory;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.plugin.PluginTestCaseBase;
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.jetbrains.jet.plugin.refactoring.changeSignature.ChangeSignaturePackage.getChangeSignatureDialog;
public class JetChangeSignatureTest extends LightCodeInsightTestCase {
public void testBadSelection() throws Exception {
configureByFile(getTestName(false) + "Before.kt");
@@ -73,6 +80,11 @@ public class JetChangeSignatureTest extends LightCodeInsightTestCase {
doTest(changeInfo);
}
public void testPreferContainedInClass() throws Exception {
JetChangeInfo changeInfo = getChangeInfo();
assertEquals("param", changeInfo.getNewParameters()[0].getName());
}
public void testAddConstructorVisibility() throws Exception {
JetChangeInfo changeInfo = getChangeInfo();
changeInfo.setNewVisibility(Visibilities.PROTECTED);
@@ -134,6 +146,13 @@ public class JetChangeSignatureTest extends LightCodeInsightTestCase {
doTest(changeInfo);
}
public void testFakeOverride() throws Exception {
JetChangeInfo changeInfo = getChangeInfo();
JetParameterInfo newParameter = new JetParameterInfo("i", KotlinBuiltIns.getInstance().getIntType());
changeInfo.addParameter(newParameter);
doTest(changeInfo);
}
public void testFunctionLiteral() throws Exception {
JetChangeInfo changeInfo = getChangeInfo();
changeInfo.getNewParameters()[1].setName("y1");
@@ -194,8 +213,15 @@ public class JetChangeSignatureTest extends LightCodeInsightTestCase {
PsiFile file = getFile();
PsiElement element = new JetChangeSignatureHandler().findTargetMember(file, editor);
assertNotNull("Target element is null", element);
JetChangeSignatureDialog dialog =
JetChangeSignatureHandler.createDialog(element, file.findElementAt(editor.getCaretModel().getOffset()), getProject(), editor);
Project project = getProject();
BindingContext bindingContext =
AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) element.getContainingFile()).getBindingContext();
PsiElement context = file.findElementAt(editor.getCaretModel().getOffset());
assertNotNull(context);
FunctionDescriptor functionDescriptor = JetChangeSignatureHandler.findDescriptor(element, project, editor, bindingContext);
assertNotNull(functionDescriptor);
JetChangeSignatureDialog dialog = getChangeSignatureDialog(project, functionDescriptor,
JetChangeSignatureHandler.getConfiguration(), bindingContext, context);
assertNotNull(dialog);
dialog.canRun();
Disposer.register(getTestRootDisposable(), dialog.getDisposable());