diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiFactory.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiFactory.java index ef7d39401ce..6ce487786b3 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiFactory.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiFactory.java @@ -45,6 +45,10 @@ public class JetPsiFactory { return property.getValOrVarNode(); } + public static ASTNode createValOrVarNode(Project project, String text) { + return createParameterList(project, "(" + text + " int x)").getParameters().get(0).getValOrVarNode(); + } + public static JetExpression createExpression(Project project, String text) { JetProperty property = createProperty(project, "val x = " + text); return property.getInitializer(); @@ -158,16 +162,25 @@ public class JetPsiFactory { return (JetSimpleNameExpression) createProperty(project, name, null, false, name).getInitializer(); } + public static PsiElement createIdentifier(Project project, String name) { + return createSimpleName(project, name).getIdentifier(); + } + public static JetNamedFunction createFunction(Project project, String funDecl) { return createDeclaration(project, funDecl, JetNamedFunction.class); } - public static JetModifierList createModifier(Project project, JetKeywordToken modifier) { + public static JetModifierList createModifierList(Project project, JetKeywordToken modifier) { String text = modifier.getValue() + " val x"; JetProperty property = createProperty(project, text); return property.getModifierList(); } + public static JetModifierList createConstructorModifierList(Project project, JetKeywordToken modifier) { + JetClass aClass = createClass(project, "class C " + modifier.getValue() + " (){}"); + return aClass.getPrimaryConstructorModifierList(); + } + public static JetExpression createEmptyBody(Project project) { JetNamedFunction function = createFunction(project, "fun foo() {}"); return function.getBodyExpression(); @@ -183,6 +196,11 @@ public class JetPsiFactory { return function.getValueParameters().get(0); } + public static JetParameterList createParameterList(Project project, String text) { + JetNamedFunction function = createFunction(project, "fun foo" + text + "{}"); + return function.getValueParameterList(); + } + @NotNull public static JetWhenEntry createWhenEntry(@NotNull Project project, @NotNull String entryText) { JetNamedFunction function = createFunction(project, "fun foo() { when(12) { " + entryText + " } }"); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetTypeParameterListOwnerStub.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetTypeParameterListOwnerStub.java index 058cf4ceb76..4e4ad3894af 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetTypeParameterListOwnerStub.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetTypeParameterListOwnerStub.java @@ -36,7 +36,7 @@ abstract class JetTypeParameterListOwnerStub extends JetNam } @Nullable - JetTypeParameterList getTypeParameterList() { + public JetTypeParameterList getTypeParameterList() { return (JetTypeParameterList) findChildByType(JetNodeTypes.TYPE_PARAMETER_LIST); } diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index eb3f4cf1136..6263b83be86 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -127,6 +127,7 @@ + diff --git a/idea/src/org/jetbrains/jet/plugin/intentions/SpecifyTypeExplicitlyAction.java b/idea/src/org/jetbrains/jet/plugin/intentions/SpecifyTypeExplicitlyAction.java index 7f2241a92f4..1010fbcd1bf 100644 --- a/idea/src/org/jetbrains/jet/plugin/intentions/SpecifyTypeExplicitlyAction.java +++ b/idea/src/org/jetbrains/jet/plugin/intentions/SpecifyTypeExplicitlyAction.java @@ -49,7 +49,10 @@ import org.jetbrains.jet.plugin.codeInsight.ReferenceToClassesShortening; import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache; import org.jetbrains.jet.renderer.DescriptorRenderer; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; public class SpecifyTypeExplicitlyAction extends PsiElementBaseIntentionAction { @NotNull @@ -187,14 +190,24 @@ public class SpecifyTypeExplicitlyAction extends PsiElementBaseIntentionAction { addTypeAnnotation(project, editor, property, anchor, exprType); } - public static void addTypeAnnotation(Project project, Editor editor, JetFunction function, @NotNull JetType exprType) { + public static void addTypeAnnotation(Project project, @Nullable Editor editor, JetFunction function, @NotNull JetType exprType) { JetParameterList valueParameterList = function.getValueParameterList(); assert valueParameterList != null; - addTypeAnnotation(project, editor, function, valueParameterList, exprType); + if (editor != null) { + addTypeAnnotation(project, editor, function, valueParameterList, exprType); + } + else { + addTypeAnnotationSilently(project, function, valueParameterList); + } } - public static void addTypeAnnotation(Project project, Editor editor, JetParameter parameter, @NotNull JetType exprType) { - addTypeAnnotation(project, editor, parameter, parameter.getNameIdentifier(), exprType); + public static void addTypeAnnotation(Project project, @Nullable Editor editor, JetParameter parameter, @NotNull JetType exprType) { + if (editor != null) { + addTypeAnnotation(project, editor, parameter, parameter.getNameIdentifier(), exprType); + } + else { + addTypeAnnotationSilently(project, parameter, parameter.getNameIdentifier()); + } } private static void addTypeAnnotation( @@ -227,10 +240,7 @@ public class SpecifyTypeExplicitlyAction extends PsiElementBaseIntentionAction { } }; - JetTypeReference typeReference = JetPsiFactory.createType(project, "Any"); - namedDeclaration.addAfter(typeReference, anchor); - Pair colon = JetPsiFactory.createColonAndWhiteSpaces(project); - namedDeclaration.addRangeAfter(colon.getFirst(), colon.getSecond(), anchor); + addTypeAnnotationSilently(project, namedDeclaration, anchor); PsiDocumentManager.getInstance(project).commitAllDocuments(); PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument()); @@ -250,6 +260,13 @@ public class SpecifyTypeExplicitlyAction extends PsiElementBaseIntentionAction { }); } + private static void addTypeAnnotationSilently(Project project, JetNamedDeclaration namedDeclaration, PsiElement anchor) { + JetTypeReference typeReference = JetPsiFactory.createType(project, "Any"); + namedDeclaration.addAfter(typeReference, anchor); + Pair colon = JetPsiFactory.createColonAndWhiteSpaces(project); + namedDeclaration.addRangeAfter(colon.getFirst(), colon.getSecond(), anchor); + } + @Nullable private static JetTypeReference getTypeRef(@NotNull JetNamedDeclaration namedDeclaration) { if (namedDeclaration instanceof JetProperty) { diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/AddModifierFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/AddModifierFix.java index afc40cbee26..162dc6b7aa3 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/AddModifierFix.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/AddModifierFix.java @@ -26,7 +26,10 @@ import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.diagnostics.Diagnostic; -import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.psi.JetModifierList; +import org.jetbrains.jet.lang.psi.JetModifierListOwner; +import org.jetbrains.jet.lang.psi.JetPropertyAccessor; +import org.jetbrains.jet.lang.psi.JetPsiFactory; import org.jetbrains.jet.lexer.JetKeywordToken; import org.jetbrains.jet.lexer.JetToken; import org.jetbrains.jet.lexer.JetTokens; @@ -88,14 +91,29 @@ public class AddModifierFix extends JetIntentionAction { @NotNull /*package*/ static JetModifierListOwner addModifier(@NotNull PsiElement element, @NotNull JetKeywordToken modifier, @Nullable JetToken[] modifiersThatCanBeReplaced, @NotNull Project project, boolean toBeginning) { JetModifierListOwner newElement = (JetModifierListOwner) (element.copy()); + changeModifier(newElement, newElement.getModifierList(), newElement.getFirstChild(), + modifiersThatCanBeReplaced, project, toBeginning, JetPsiFactory.createModifierList(project, modifier)); + return newElement; + } - JetModifierList modifierList = newElement.getModifierList(); - JetModifierList listWithModifier = JetPsiFactory.createModifier(project, modifier); + public static void changeModifier( + PsiElement element, @Nullable JetModifierList modifierList, @Nullable PsiElement insertAnchor, + JetToken[] modifiersThatCanBeReplaced, Project project, boolean toBeginning, JetModifierList listWithModifier + ) { PsiElement whiteSpace = JetPsiFactory.createWhiteSpace(project); if (modifierList == null) { - PsiElement firstChild = newElement.getFirstChild(); - newElement.addBefore(listWithModifier, firstChild); - newElement.addBefore(whiteSpace, firstChild); + if (listWithModifier != null) { + if (insertAnchor != null) { + listWithModifier = (JetModifierList) element.addBefore(listWithModifier, insertAnchor); + element.addBefore(whiteSpace, insertAnchor); + element.addBefore(whiteSpace, listWithModifier); + } + else { + PsiElement firstChild = element.getFirstChild(); + element.addBefore(listWithModifier, firstChild); + element.addBefore(whiteSpace, firstChild); + } + } } else { boolean replaced = false; @@ -106,7 +124,7 @@ public class AddModifierFix extends JetIntentionAction { if (modifierList.hasModifier(modifierThatCanBeReplaced)) { PsiElement modifierElement = modifierList.getModifierNode(modifierThatCanBeReplaced).getPsi(); assert modifierElement != null; - if (!replaced) { + if (!replaced && listWithModifier != null) { toBeReplaced = modifierElement; toReplace = listWithModifier.getFirstChild(); //modifierElement.replace(listWithModifier.getFirstChild()); @@ -121,7 +139,7 @@ public class AddModifierFix extends JetIntentionAction { toBeReplaced.replace(toReplace); } } - if (!replaced) { + if (!replaced && listWithModifier != null) { if (toBeginning) { PsiElement firstChild = modifierList.getFirstChild(); modifierList.addBefore(listWithModifier.getFirstChild(), firstChild); @@ -134,7 +152,6 @@ public class AddModifierFix extends JetIntentionAction { } } } - return newElement; } @NotNull diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeFunctionReturnTypeFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeFunctionReturnTypeFix.java index 246d3c322e5..f27f58cecf8 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeFunctionReturnTypeFix.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeFunctionReturnTypeFix.java @@ -77,21 +77,25 @@ public class ChangeFunctionReturnTypeFix extends JetIntentionAction typeWhiteSpaceAndColon = JetPsiFactory.createTypeWhiteSpaceAndColon(project, type.toString()); - element.addRangeAfter(typeWhiteSpaceAndColon.first, typeWhiteSpaceAndColon.second, elementToPrecedeType); + addReturnTypeAnnotation(project, element, type.toString()); } } + public static void addReturnTypeAnnotation(Project project, JetFunction function, String typeText) { + PsiElement elementToPrecedeType = function.getValueParameterList(); + if (elementToPrecedeType == null) elementToPrecedeType = function.getNameIdentifier(); + assert elementToPrecedeType != null : "Return type of function without name can't mismatch anything"; + if (elementToPrecedeType.getNextSibling() instanceof PsiErrorElement) { + // if a function doesn't have a value parameter list, a syntax error is raised, and it should follow the function name + elementToPrecedeType = elementToPrecedeType.getNextSibling(); + } + Pair typeWhiteSpaceAndColon = JetPsiFactory.createTypeWhiteSpaceAndColon(project, typeText); + function.addRangeAfter(typeWhiteSpaceAndColon.first, typeWhiteSpaceAndColon.second, elementToPrecedeType); + } + @NotNull public static JetMultiDeclarationEntry getMultiDeclarationEntryThatTypeMismatchComponentFunction(Diagnostic diagnostic) { String componentName = ((DiagnosticWithParameters3) diagnostic).getA().getName(); diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeVisibilityModifierFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeVisibilityModifierFix.java index 1a96bf2323d..0bcc32fbdd9 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeVisibilityModifierFix.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeVisibilityModifierFix.java @@ -22,19 +22,25 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.descriptors.Visibilities; +import org.jetbrains.jet.lang.descriptors.Visibility; import org.jetbrains.jet.lang.diagnostics.Diagnostic; import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.psi.JetModifierListOwner; import org.jetbrains.jet.lang.psi.JetParameter; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lexer.JetKeywordToken; -import org.jetbrains.jet.lexer.JetToken; import org.jetbrains.jet.lexer.JetTokens; import org.jetbrains.jet.plugin.JetBundle; import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache; +import org.jetbrains.jet.plugin.refactoring.JetRefactoringUtil; public class ChangeVisibilityModifierFix extends JetIntentionAction { + public static final JetKeywordToken[] VISIBILITY_TOKENS = + new JetKeywordToken[] {JetTokens.PUBLIC_KEYWORD, JetTokens.PRIVATE_KEYWORD, JetTokens.PROTECTED_KEYWORD, JetTokens.INTERNAL_KEYWORD}; + public ChangeVisibilityModifierFix(@NotNull JetModifierListOwner element) { super(element); } @@ -61,8 +67,7 @@ public class ChangeVisibilityModifierFix extends JetIntentionAction newParameters; + private final PsiElement context; + private final JetGeneratedInfo generatedInfo; + private Boolean parameterNamesChanged; + + public JetChangeInfo( + JetFunctionPlatformDescriptor oldDescriptor, + String newName, + JetType newReturnType, + String newReturnTypeText, + Visibility newVisibility, + List newParameters, + PsiElement context, + JetGeneratedInfo generatedInfo + ) { + this.oldDescriptor = oldDescriptor; + this.newName = newName; + this.newReturnType = newReturnType; + this.newReturnTypeText = newReturnTypeText; + this.newVisibility = newVisibility; + this.newParameters = newParameters; + this.context = context; + this.generatedInfo = generatedInfo; + } + + public String getNewSignature(@Nullable JetFunction inheritedFunction, boolean isInherited) { + StringBuilder buffer = new StringBuilder(); + + if (isConstructor()) { + buffer.append(newName); + + if (newVisibility != null && newVisibility != Visibilities.PUBLIC) + buffer.append(' ').append(newVisibility.toString()).append(' '); + } + else { + if (newVisibility != null && newVisibility != Visibilities.INTERNAL) + buffer.append(newVisibility.toString()).append(' '); + + buffer.append(JetTokens.FUN_KEYWORD).append(' ').append(newName); + } + + buffer.append(getNewParametersSignature(inheritedFunction, isInherited, buffer.length())); + + if (newReturnType != null && !KotlinBuiltIns.getInstance().isUnit(newReturnType) && !isConstructor()) + buffer.append(": ").append(newReturnTypeText); + + return buffer.toString(); + } + + public String getNewParametersSignature(PsiElement inheritedFunction, boolean isInherited, int indentLength) { + StringBuilder buffer = new StringBuilder("("); + String indent = StringUtil.repeatSymbol(' ', indentLength + 1); + + for (int i = 0; i < newParameters.size(); i++) { + JetParameterInfo parameterInfo = newParameters.get(i); + if (i > 0) { + buffer.append(","); + buffer.append("\n"); + buffer.append(indent); + } + + buffer.append(parameterInfo.getDeclarationSignature(isInherited, inheritedFunction, oldDescriptor)); + } + + buffer.append(")"); + return buffer.toString(); + } + + private boolean innerParameterSetOrOrderChanged() { + if (newParameters.size() != oldDescriptor.getParametersCount()) + return true; + + for (int i = 0; i < newParameters.size(); i ++) { + if (newParameters.get(i).getOldIndex() != i) + return true; + } + + return false; + } + + @NotNull + @Override + public JetParameterInfo[] getNewParameters() { + return newParameters.toArray(new JetParameterInfo[newParameters.size()]); + } + + public void setNewParameter(int index, JetParameterInfo parameterInfo) { + newParameters.set(index, parameterInfo); + } + + public void addParameter(JetParameterInfo parameterInfo) { + newParameters.add(parameterInfo); + } + + @Override + public boolean isParameterSetOrOrderChanged() { + if (parameterNamesChanged == null) + parameterNamesChanged = innerParameterSetOrOrderChanged(); + + return parameterNamesChanged; + } + + @Override + public boolean isParameterTypesChanged() { + return true; + } + + @Override + public boolean isParameterNamesChanged() { + return true; + } + + @Override + public boolean isGenerateDelegate() { + return false; + } + + public void setNewName(String newName) { + this.newName = newName; + } + + @Override + public boolean isNameChanged() { + return !newName.equals(oldDescriptor.getName()); + } + + public boolean isVisibilityChanged() { + return !newVisibility.equals(oldDescriptor.getVisibility()); + } + + public Visibility getNewVisibility() { + return newVisibility; + } + + public void setNewVisibility(Visibility newVisibility) { + this.newVisibility = newVisibility; + } + + @Override + public PsiElement getMethod() { + return oldDescriptor.getMethod(); + } + + @Nullable + public FunctionDescriptor getOldDescriptor() { + return oldDescriptor.getDescriptor(); + } + + public JetFunctionPlatformDescriptor getFunctionDescriptor() { + return oldDescriptor; + } + + public boolean isConstructor() { + return oldDescriptor.isConstructor(); + } + + public String getNewReturnTypeText() { + return newReturnTypeText; + } + + public void setNewReturnTypeText(String newReturnTypeText) { + this.newReturnTypeText = newReturnTypeText; + } + + @Override + public boolean isReturnTypeChanged() { + return !newReturnTypeText.equals(oldDescriptor.getReturnTypeText()); + } + + @Nullable + public String getOldName() { + PsiElement function = oldDescriptor.getMethod(); + return function instanceof JetFunction ? ((JetFunction) function).getName() : null; + } + + @Override + public String getNewName() { + return newName; + } + + public JetGeneratedInfo getGeneratedInfo() { + return generatedInfo; + } + + public PsiElement getContext() { + return context; + } + + @Override + public Language getLanguage() { + return JetLanguage.INSTANCE; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetChangeSignatureDialog.java b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetChangeSignatureDialog.java new file mode 100644 index 00000000000..9f62cac39e0 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetChangeSignatureDialog.java @@ -0,0 +1,419 @@ +/* + * 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.openapi.editor.Document; +import com.intellij.openapi.editor.colors.EditorColorsManager; +import com.intellij.openapi.editor.colors.EditorFontType; +import com.intellij.openapi.editor.event.DocumentAdapter; +import com.intellij.openapi.editor.event.DocumentEvent; +import com.intellij.openapi.fileTypes.LanguageFileType; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.VerticalFlowLayout; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiCodeFragment; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiElement; +import com.intellij.refactoring.BaseRefactoringProcessor; +import com.intellij.refactoring.changeSignature.CallerChooserBase; +import com.intellij.refactoring.changeSignature.ChangeSignatureDialogBase; +import com.intellij.refactoring.changeSignature.MethodDescriptor; +import com.intellij.refactoring.changeSignature.ParameterTableModelItemBase; +import com.intellij.refactoring.ui.ComboBoxVisibilityPanel; +import com.intellij.refactoring.ui.VisibilityPanelBase; +import com.intellij.ui.DottedBorder; +import com.intellij.ui.EditorTextField; +import com.intellij.ui.components.JBLabel; +import com.intellij.ui.treeStructure.Tree; +import com.intellij.util.Consumer; +import com.intellij.util.Function; +import com.intellij.util.IJSwingUtilities; +import com.intellij.util.ui.ColumnInfo; +import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.table.JBTableRow; +import com.intellij.util.ui.table.JBTableRowEditor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.Visibilities; +import org.jetbrains.jet.lang.descriptors.Visibility; +import org.jetbrains.jet.lang.psi.JetPsiFactory; +import org.jetbrains.jet.lang.psi.JetTypeCodeFragment; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.plugin.JetFileType; +import org.jetbrains.jet.plugin.refactoring.JetRefactoringBundle; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +public class JetChangeSignatureDialog extends ChangeSignatureDialogBase< + JetParameterInfo, + PsiElement, + Visibility, + JetFunctionPlatformDescriptor, + ParameterTableModelItemBase, + JetFunctionParameterTableModel + > +{ + 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); + this.commandName = commandName; + } + + @Override + protected LanguageFileType getFileType() { + return JetFileType.INSTANCE; + } + + @Override + protected JetFunctionParameterTableModel createParametersInfoModel(JetFunctionPlatformDescriptor descriptor) { + if (descriptor.isConstructor()) + return new JetConstructorParameterTableModel(myDefaultValueContext); + else + return new JetFunctionParameterTableModel(myDefaultValueContext); + } + + @Override + protected PsiCodeFragment createReturnTypeCodeFragment() { + return JetPsiFactory.createTypeCodeFragment(myProject, myMethod.getReturnTypeText(), myMethod.getContext()); + } + + @Nullable + public JetType getReturnType() { + return getType((JetTypeCodeFragment) myReturnTypeCodeFragment); + } + + private static JetType getType(JetTypeCodeFragment typeCodeFragment) { + return typeCodeFragment != null ? typeCodeFragment.getType() : null; + } + + @Override + protected JComponent getRowPresentation(ParameterTableModelItemBase item, boolean selected, boolean focused) { + JPanel panel = new JPanel(new BorderLayout()); + String valOrVar = ""; + + if (myMethod.isConstructor()) { + switch (item.parameter.getValOrVar()) { + case None: + valOrVar = " "; + break; + case Val: + valOrVar = "val "; + break; + case Var: + valOrVar = "var "; + break; + } + } + + String parameterName = item.parameter.getName(); + String typeText = item.typeCodeFragment.getText(); + String defaultValue = item.defaultValueCodeFragment.getText(); + String separator = StringUtil.repeatSymbol(' ', getParamNamesMaxLength() - parameterName.length() + 1); + String text = valOrVar + parameterName + ":" + separator + typeText; + + if (StringUtil.isNotEmpty(defaultValue)) + text += " // default value = " + defaultValue; + + EditorTextField field = new EditorTextField(" " + text, getProject(), getFileType()) { + @Override + protected boolean shouldHaveBorder() { + return false; + } + }; + + Font font = EditorColorsManager.getInstance().getGlobalScheme().getFont(EditorFontType.PLAIN); + font = new Font(font.getFontName(), font.getStyle(), 12); + field.setFont(font); + + if (selected && focused) { + panel.setBackground(UIUtil.getTableSelectionBackground()); + field.setAsRendererWithSelection(UIUtil.getTableSelectionBackground(), UIUtil.getTableSelectionForeground()); + } else { + panel.setBackground(UIUtil.getTableBackground()); + if (selected && !focused) { + panel.setBorder(new DottedBorder(UIUtil.getTableForeground())); + } + } + panel.add(field, BorderLayout.WEST); + return panel; + } + + private int getColumnTextMaxLength(Function, String> nameFunction) { + int len = 0; + for (ParameterTableModelItemBase item : myParametersTableModel.getItems()) { + String text = nameFunction.fun(item); + len = Math.max(len, text == null ? 0 : text.length()); + } + return len; + } + + private int getParamNamesMaxLength() { + return getColumnTextMaxLength(new Function, String>() { + @Override + public String fun(ParameterTableModelItemBase item) { + return item.parameter == null ? null : item.parameter.getName(); + } + }); + } + + private int getTypesMaxLength() { + return getColumnTextMaxLength(new Function, String>() { + @Override + public String fun(ParameterTableModelItemBase item) { + return item.typeCodeFragment == null ? null : item.typeCodeFragment.getText(); + } + }); + } + + private int getDefaultValuesMaxLength() { + return getColumnTextMaxLength(new Function, String>() { + @Override + public String fun(ParameterTableModelItemBase item) { + return item.defaultValueCodeFragment == null ? null : item.defaultValueCodeFragment.getText(); + } + }); + } + + @Override + protected boolean isListTableViewSupported() { + return true; + } + + @Override + protected boolean isEmptyRow(ParameterTableModelItemBase row) { + if (!StringUtil.isEmpty(row.parameter.getName())) return false; + if (!StringUtil.isEmpty(row.parameter.getTypeText())) return false; + return true; + } + + @Nullable + @Override + protected CallerChooserBase createCallerChooser(String title, Tree treeToReuse, Consumer> callback) { + throw new UnsupportedOperationException(); + } + + @Override + protected JBTableRowEditor getTableEditor(final JTable t, final ParameterTableModelItemBase item) { + return new JBTableRowEditor() { + private final List components = new ArrayList(); + private final boolean defaultValueColumnEnabled = item.parameter.isNewParameter(); + + @Override + public void prepareEditor(JTable table, final int row) { + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + int column = 0; + + for (ColumnInfo columnInfo : myParametersTableModel.getColumnInfos()) { + JPanel panel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.TOP, 4, 2, true, false)); + EditorTextField editor = null; + JComponent component; + final int columnFinal = column; + + if (JetFunctionParameterTableModel.isTypeColumn(columnInfo)) { + Document document = PsiDocumentManager.getInstance(getProject()).getDocument(item.typeCodeFragment); + component = editor = new EditorTextField(document, getProject(), getFileType()); + } + else if (JetFunctionParameterTableModel.isNameColumn(columnInfo)) + component = editor = new EditorTextField((String)columnInfo.valueOf(item), getProject(), getFileType()); + else if (JetFunctionParameterTableModel.isDefaultValueColumn(columnInfo) && defaultValueColumnEnabled) { + Document document = PsiDocumentManager.getInstance(getProject()).getDocument(item.defaultValueCodeFragment); + component = editor = new EditorTextField(document, getProject(), getFileType()); + } + else if (JetConstructorParameterTableModel.isValVarColumn(columnInfo)) { + JComboBox comboBox = new JComboBox(JetValVar.values()); + comboBox.setSelectedItem(item.parameter.getValOrVar()); + comboBox.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(@NotNull ItemEvent e) { + myParametersTableModel.setValueAtWithoutUpdate(e.getItem(), row, columnFinal); + updateSignature(); + } + }); + component = comboBox; + } + else + continue; + + JBLabel label = new JBLabel(columnInfo.getName(), UIUtil.ComponentStyle.SMALL); + panel.add(label); + + if (editor != null) { + editor.addDocumentListener(new DocumentAdapter() { + @Override + public void documentChanged(DocumentEvent e) { + fireDocumentChanged(e, columnFinal); + } + }); + editor.setPreferredWidth(t.getWidth() / myParametersTableModel.getColumnCount()); + } + + components.add(component); + panel.add(component); + add(panel); + IJSwingUtilities.adjustComponentsOnMac(label, component); + column++; + } + } + + @Override + public JBTableRow getValue() { + return new JBTableRow() { + @Override + public Object getValueAt(int column) { + ColumnInfo columnInfo = myParametersTableModel.getColumnInfos()[column]; + + if (JetConstructorParameterTableModel.isValVarColumn(columnInfo)) + return ((JComboBox) components.get(column)).getSelectedItem(); + else if (JetFunctionParameterTableModel.isTypeColumn(columnInfo)) + return item.typeCodeFragment; + else if (JetFunctionParameterTableModel.isNameColumn(columnInfo)) + return ((EditorTextField) components.get(column)).getText(); + else if (JetFunctionParameterTableModel.isDefaultValueColumn(columnInfo)) + return item.defaultValueCodeFragment; + else + return null; + } + }; + } + + private int getColumnWidth(int letters) { + Font font = EditorColorsManager.getInstance().getGlobalScheme().getFont(EditorFontType.PLAIN); + font = new Font(font.getFontName(), font.getStyle(), 12); + return letters * Toolkit.getDefaultToolkit().getFontMetrics(font).stringWidth("W"); + } + + private int getEditorIndex(int x) { + int[] columnLetters = defaultValueColumnEnabled ? + new int[] { 4, getParamNamesMaxLength(), getTypesMaxLength(), getDefaultValuesMaxLength() } : + new int[] { 4, getParamNamesMaxLength(), getTypesMaxLength() }; + int columnIndex = 0; + + for (int i = myMethod.isConstructor() ? 0 : 1; i < columnLetters.length; i ++) { + int width = getColumnWidth(columnLetters[i]); + + if (x <= width) + return columnIndex; + + columnIndex ++; + x -= width; + } + + return columnIndex - 1; + } + + @Override + public JComponent getPreferredFocusedComponent() { + MouseEvent me = getMouseEvent(); + int index = me != null ? getEditorIndex((int) me.getPoint().getX()) : myMethod.isConstructor() ? 1 : 0; + JComponent component = components.get(index); + return component instanceof EditorTextField ? ((EditorTextField) component).getFocusTarget() : component; + } + + @Override + public JComponent[] getFocusableComponents() { + JComponent[] focusable = new JComponent[components.size()]; + + for (int i = 0; i < components.size(); i++) { + focusable[i] = components.get(i); + + if (focusable[i] instanceof EditorTextField) + focusable[i] = ((EditorTextField) focusable[i]).getFocusTarget(); + } + + return focusable; + } + }; + } + + @Override + protected String calculateSignature() { + JetChangeInfo changeInfo = evaluateChangeInfo(); + return changeInfo.getNewSignature(null, false); + } + + @Override + protected VisibilityPanelBase createVisibilityControl() { + return new ComboBoxVisibilityPanel(new Visibility[]{ + Visibilities.INTERNAL, Visibilities.PRIVATE, Visibilities.PROTECTED, Visibilities.PUBLIC } + ); + } + + @Override + protected void updateSignatureAlarmFired() { + super.updateSignatureAlarmFired(); + validateButtons(); + } + + @Nullable + @Override + protected String validateAndCommitData() { + return null; + } + + @Override + protected void canRun() throws ConfigurationException { + if (myNamePanel.isVisible() && myMethod.canChangeName() && !JavaPsiFacade.getInstance(myProject).getNameHelper().isIdentifier(getMethodName())) + throw new ConfigurationException(JetRefactoringBundle.message("function.name.is.invalid")); + if (myMethod.canChangeReturnType() == MethodDescriptor.ReadWriteOption.ReadWrite && getReturnType() == null) + throw new ConfigurationException(JetRefactoringBundle.message("return.type.is.invalid")); + + List> parameterInfos = myParametersTableModel.getItems(); + + for (ParameterTableModelItemBase item : parameterInfos) { + String parameterName = item.parameter.getName(); + + if (!JavaPsiFacade.getInstance(myProject).getNameHelper().isIdentifier(parameterName)) + throw new ConfigurationException(JetRefactoringBundle.message("parameter.name.is.invalid", parameterName)); + if (getType((JetTypeCodeFragment) item.typeCodeFragment) == null) + throw new ConfigurationException(JetRefactoringBundle.message("parameter.type.is.invalid", item.typeCodeFragment.getText())); + } + } + + @Override + protected BaseRefactoringProcessor createRefactoringProcessor() { + return new JetChangeSignatureProcessor(myProject, evaluateChangeInfo(), commandName != null ? commandName : getTitle()); + } + + public JetChangeInfo evaluateChangeInfo() { + List parameters = getParameters(); + + for (int i = 0; i < parameters.size(); i++) { + JetParameterInfo parameter = parameters.get(i); + parameter.setTypeText(myParametersTableModel.getItems().get(i).typeCodeFragment.getText().trim()); + parameter.setDefaultValueText(myParametersTableModel.getItems().get(i).defaultValueCodeFragment.getText().trim()); + } + + String returnTypeText = myReturnTypeCodeFragment != null ? myReturnTypeCodeFragment.getText().trim() : ""; + return new JetChangeInfo(myMethod, getMethodName(), getReturnType(), returnTypeText, + getVisibility(), parameters, myDefaultValueContext, generatedInfo); + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetChangeSignatureHandler.java b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetChangeSignatureHandler.java new file mode 100644 index 00000000000..ecd274ba08f --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetChangeSignatureHandler.java @@ -0,0 +1,155 @@ +/* + * 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.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.ScrollType; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.refactoring.HelpID; +import com.intellij.refactoring.RefactoringBundle; +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.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; + +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) { + if (PsiTreeUtil.getParentOfType(element, JetParameterList.class) != null) { + return PsiTreeUtil.getParentOfType(element, JetFunction.class, JetClass.class); + } + + JetTypeParameterList typeParameterList = PsiTreeUtil.getParentOfType(element, JetTypeParameterList.class); + if (typeParameterList != null) { + return PsiTreeUtil.getParentOfType(typeParameterList, JetFunction.class, JetClass.class); + } + + PsiElement elementParent = element.getParent(); + if (elementParent instanceof JetNamedFunction && ((JetNamedFunction)elementParent).getNameIdentifier()==element) { + return elementParent; + } + 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 (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; + } + } + } + } + + return null; + } + + @Override + public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) { + editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE); + PsiElement element = findTargetMember(file, editor); + if (element == null) { + 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(); + } + } + } + + @Override + 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(); + } + } + + @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); + + if (descriptor instanceof ClassDescriptor) { + descriptor = ((ClassDescriptor) descriptor).getUnsubstitutedPrimaryConstructor(); + } + if (descriptor instanceof FunctionDescriptorImpl) { + 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); + return null; + } + } + + return new JetChangeSignatureDialog(project, new JetFunctionPlatformDescriptorImpl((FunctionDescriptor) descriptor, element), context); + } + else { + String message = RefactoringBundle.getCannotRefactorMessage(JetRefactoringBundle.message( + "error.wrong.caret.position.function.or.constructor.name")); + CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.CHANGE_SIGNATURE); + return null; + } + } + + @Nullable + @Override + public String getTargetNotFoundMessage() { + return JetRefactoringBundle.message("error.wrong.caret.position.function.or.constructor.name"); + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetChangeSignatureProcessor.java b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetChangeSignatureProcessor.java new file mode 100644 index 00000000000..ac5d3f68992 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetChangeSignatureProcessor.java @@ -0,0 +1,124 @@ +/* + * 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.openapi.application.ApplicationManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; +import com.intellij.psi.PsiElement; +import com.intellij.refactoring.RefactoringBundle; +import com.intellij.refactoring.changeSignature.ChangeSignatureProcessorBase; +import com.intellij.refactoring.changeSignature.ChangeSignatureUsageProcessor; +import com.intellij.refactoring.rename.RenameUtil; +import com.intellij.refactoring.ui.ConflictsDialog; +import com.intellij.usageView.UsageInfo; +import com.intellij.usageView.UsageViewDescriptor; +import com.intellij.util.containers.HashSet; +import com.intellij.util.containers.MultiMap; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +public class JetChangeSignatureProcessor extends ChangeSignatureProcessorBase { + private final String commandName; + + public JetChangeSignatureProcessor(Project project, JetChangeInfo changeInfo, String commandName) { + super(project, changeInfo); + this.commandName = commandName; + } + + @NotNull + @Override + protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usages) { + String subject = getChangeInfo().isConstructor() ? "constructor" : "function"; + return new JetUsagesViewDescriptor(myChangeInfo.getMethod(), RefactoringBundle.message("0.to.change.signature", subject)); + } + + @Override + public JetChangeInfo getChangeInfo() { + return (JetChangeInfo) super.getChangeInfo(); + } + + @Override + protected boolean preprocessUsages(Ref refUsages) { + for (ChangeSignatureUsageProcessor processor : ChangeSignatureUsageProcessor.EP_NAME.getExtensions()) { + if (!processor.setupDefaultValues(myChangeInfo, refUsages, myProject)) return false; + } + MultiMap conflictDescriptions = new MultiMap(); + for (ChangeSignatureUsageProcessor usageProcessor : ChangeSignatureUsageProcessor.EP_NAME.getExtensions()) { + MultiMap conflicts = usageProcessor.findConflicts(myChangeInfo, refUsages); + for (PsiElement key : conflicts.keySet()) { + Collection collection = conflictDescriptions.get(key); + if (collection.size() == 0) collection = new HashSet(); + collection.addAll(conflicts.get(key)); + conflictDescriptions.put(key, collection); + } + } + + UsageInfo[] usagesIn = refUsages.get(); + RenameUtil.addConflictDescriptions(usagesIn, conflictDescriptions); + Set usagesSet = new HashSet(Arrays.asList(usagesIn)); + RenameUtil.removeConflictUsages(usagesSet); + if (!conflictDescriptions.isEmpty()) { + if (ApplicationManager.getApplication().isUnitTestMode()) { + throw new ConflictsInTestsException(conflictDescriptions.values()); + } + if (myPrepareSuccessfulSwingThreadCallback != null) { + ConflictsDialog dialog = prepareConflictsDialog(conflictDescriptions, usagesIn); + dialog.show(); + if (!dialog.isOK()) { + if (dialog.isShowConflicts()) prepareSuccessful(); + return false; + } + } + } + + UsageInfo[] array = usagesSet.toArray(new UsageInfo[usagesSet.size()]); + Arrays.sort(array, new Comparator() { + @Override + public int compare(@NotNull UsageInfo u1, @NotNull UsageInfo u2) { + PsiElement element1 = u1.getElement(); + PsiElement element2 = u2.getElement(); + int rank1 = element1 != null ? element1.getTextOffset() : -1; + int rank2 = element2 != null ? element2.getTextOffset() : -1; + return rank2 - rank1; // Reverse order + } + }); + refUsages.set(array); + prepareSuccessful(); + return true; + } + + @Override + protected boolean isPreviewUsages(UsageInfo[] usages) { + return isPreviewUsages(); + } + + @NotNull + @Override + protected Collection getElementsToWrite(@NotNull UsageViewDescriptor descriptor) { + Collection elements = new ArrayList(); + elements.addAll(super.getElementsToWrite(descriptor)); + elements.addAll(getChangeInfo().getGeneratedInfo().getFilesToWrite()); + return elements; + } + + @Override + protected String getCommandName() { + return commandName; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetChangeSignatureUsageProcessor.java b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetChangeSignatureUsageProcessor.java new file mode 100644 index 00000000000..6bccdd75235 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetChangeSignatureUsageProcessor.java @@ -0,0 +1,252 @@ +/* + * 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.openapi.project.Project; +import com.intellij.openapi.util.Ref; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.refactoring.changeSignature.ChangeInfo; +import com.intellij.refactoring.changeSignature.ChangeSignatureUsageProcessor; +import com.intellij.refactoring.changeSignature.ParameterInfo; +import com.intellij.refactoring.rename.ResolveSnapshotProvider; +import com.intellij.refactoring.util.TextOccurrencesUtil; +import com.intellij.usageView.UsageInfo; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.HashSet; +import com.intellij.util.containers.MultiMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.*; +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.lang.resolve.name.Name; +import org.jetbrains.jet.lang.resolve.scopes.JetScope; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.TypeProjection; +import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache; +import org.jetbrains.jet.plugin.refactoring.changeSignature.usages.JetFunctionCallUsage; +import org.jetbrains.jet.plugin.refactoring.changeSignature.usages.JetFunctionDefinitionUsage; +import org.jetbrains.jet.plugin.refactoring.changeSignature.usages.JetParameterUsage; +import org.jetbrains.jet.plugin.refactoring.changeSignature.usages.JetUsageInfo; +import org.jetbrains.jet.renderer.DescriptorRenderer; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsageProcessor { + @Override + public UsageInfo[] findUsages(ChangeInfo info) { + Set result = new HashSet(); + findAllMethodUsages((JetChangeInfo)info, result); + return result.toArray(new UsageInfo[result.size()]); + } + + private static void findAllMethodUsages(JetChangeInfo changeInfo, Set result) { + PsiElement method = changeInfo.getMethod(); + + if (method != null) + findOneMethodUsages(method, changeInfo, result, false); + + //TODO overridden methods, tests for different parameter names in overridden methods + } + + private static void findOneMethodUsages(@NotNull PsiElement functionPsi, JetChangeInfo changeInfo, + Set result, boolean isInherited) { + result.add(new JetFunctionDefinitionUsage(functionPsi, isInherited)); + + for (PsiReference reference : ReferencesSearch.search(functionPsi, functionPsi.getUseScope())) { + PsiElement element = reference.getElement(); + PsiElement parent = element.getParent(); + + if (parent instanceof JetReferenceExpression) { + parent = parent.getParent(); + + if (parent instanceof JetCallExpression) + result.add(new JetFunctionCallUsage((JetCallExpression) parent, functionPsi, isInherited)); + else if (parent instanceof JetUserType && parent.getParent() instanceof JetTypeReference) { + parent = parent.getParent().getParent(); + + if (parent instanceof JetConstructorCalleeExpression && parent.getParent() instanceof JetDelegatorToSuperCall) + result.add(new JetFunctionCallUsage((JetDelegatorToSuperCall)parent.getParent(), functionPsi, isInherited)); + } + } + } + + String oldName = changeInfo.getOldName(); + + if (oldName != null) + TextOccurrencesUtil.findNonCodeUsages(functionPsi, oldName, true, true, changeInfo.getNewName(), result); + + List oldParameters = functionPsi instanceof JetFunction + ? ((JetFunction) functionPsi).getValueParameters() + : ((JetClass) functionPsi).getPrimaryConstructorParameters(); + + for (JetParameterInfo parameterInfo : changeInfo.getNewParameters()) { + if (parameterInfo.getOldIndex() >= 0 && parameterInfo.getOldIndex() < oldParameters.size()) { + JetParameter oldParam = oldParameters.get(parameterInfo.getOldIndex()); + String oldParamName = oldParam.getName(); + + if (oldParamName != null && !oldParamName.equals(parameterInfo.getName())) { + for (PsiReference reference : ReferencesSearch.search(oldParam, oldParam.getUseScope())) { + PsiElement element = reference.getElement(); + + if (element.getParent() instanceof JetSimpleNameExpression) + result.add(new JetParameterUsage((JetSimpleNameExpression) element.getParent(), parameterInfo, functionPsi, isInherited)); + } + } + } + } + } + + @Override + public MultiMap findConflicts(ChangeInfo info, Ref refUsages) { + MultiMap result = new MultiMap(); + Set parameterNames = new HashSet(); + JetChangeInfo changeInfo = (JetChangeInfo) info; + PsiElement function = info.getMethod(); + PsiElement element = function != null ? function : changeInfo.getContext(); + BindingContext bindingContext = AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile)element.getContainingFile()).getBindingContext(); + FunctionDescriptor oldDescriptor = changeInfo.getOldDescriptor(); + JetScope parametersScope = null; + DeclarationDescriptor containingDeclaration = oldDescriptor != null ? oldDescriptor.getContainingDeclaration() : null; + + if (oldDescriptor instanceof ConstructorDescriptor && containingDeclaration instanceof ClassDescriptor) + parametersScope = ((ClassDescriptor) containingDeclaration).getMemberScope(Collections.emptyList()); + else if (function instanceof JetFunction) + parametersScope = getFunctionBodyScope((JetFunction) function, bindingContext); + + JetScope functionScope = getFunctionScope(bindingContext, containingDeclaration); + + if (!changeInfo.isConstructor() && functionScope != null && !info.getNewName().isEmpty()) { + for (FunctionDescriptor conflict : functionScope.getFunctions(Name.identifier(info.getNewName()))) { + if (conflict != oldDescriptor && getFunctionParameterTypes(conflict).equals(getFunctionParameterTypes(oldDescriptor))) { + PsiElement conflictElement = BindingContextUtils.descriptorToDeclaration(bindingContext, conflict); + result.putValue(conflictElement, "Function already exists: '" + DescriptorRenderer.SHORT_NAMES_IN_TYPES.render(conflict) + "'"); + break; + } + } + } + + for (ParameterInfo parameter : info.getNewParameters()) { + JetValVar valOrVar = ((JetParameterInfo) parameter).getValOrVar(); + String parameterName = parameter.getName(); + + if (!parameterNames.add(parameterName)) { + result.putValue(element, "Duplicating parameter '" + parameterName + "'"); + } + if (parametersScope != null) { + if (changeInfo.isConstructor() && valOrVar != JetValVar.None) { + for (VariableDescriptor property : parametersScope.getProperties(Name.identifier(parameterName))) { + PsiElement propertyDeclaration = BindingContextUtils.descriptorToDeclaration(bindingContext, property); + + if (propertyDeclaration != null && !(propertyDeclaration.getParent() instanceof JetParameterList)) { + result.putValue(propertyDeclaration, "Duplicating property '" + parameterName + "'"); + break; + } + } + } + else if (function instanceof JetFunction) { + VariableDescriptor variable = parametersScope.getLocalVariable(Name.identifier(parameterName)); + + if (variable != null && !(variable instanceof ValueParameterDescriptor)) { + PsiElement conflictElement = BindingContextUtils.descriptorToDeclaration(bindingContext, variable); + result.putValue(conflictElement, "Duplicating local variable '" + parameterName + "'"); + } + } + } + } + + return result; + } + + @Nullable + private static JetScope getFunctionScope(BindingContext bindingContext, DeclarationDescriptor containingDeclaration) { + if (containingDeclaration instanceof ClassDescriptor) + return ((ClassDescriptor) containingDeclaration).getMemberScope(ContainerUtil.emptyList()); + else if (containingDeclaration instanceof FunctionDescriptorImpl) { + PsiElement container = BindingContextUtils.descriptorToDeclaration(bindingContext, containingDeclaration); + + if (container instanceof JetFunction) + return getFunctionBodyScope((JetFunction) container, bindingContext); + } + else if (containingDeclaration instanceof NamespaceDescriptor) + return ((NamespaceDescriptor) containingDeclaration).getMemberScope(); + + return null; + } + + @Nullable + static JetScope getFunctionBodyScope(JetFunction element, BindingContext bindingContext) { + JetExpression body = element.getBodyExpression(); + + if (body != null) { + for (PsiElement child : body.getChildren()) { + if (child instanceof JetExpression) + return bindingContext.get(BindingContext.RESOLUTION_SCOPE, (JetExpression)child); + } + } + + return null; + } + + private static List getFunctionParameterTypes(FunctionDescriptor descriptor) { + return ContainerUtil.map(descriptor.getValueParameters(), new Function() { + @Override + public JetType fun(ValueParameterDescriptor descriptor) { + return descriptor.getType(); + } + }); + } + + @Override + public boolean processUsage(ChangeInfo changeInfo, UsageInfo usageInfo, boolean beforeMethodChange, UsageInfo[] usages) { + if (beforeMethodChange) + return true; + + PsiElement element = usageInfo.getElement(); + + if (element != null) + return usageInfo instanceof JetUsageInfo ? ((JetUsageInfo) usageInfo).processUsage((JetChangeInfo) changeInfo, element) : true; + else + return false; + } + + @Override + public boolean processPrimaryMethod(ChangeInfo changeInfo) { + return true; + } + + @Override + public boolean shouldPreviewUsages(ChangeInfo changeInfo, UsageInfo[] usages) { + return false; + } + + @Override + public boolean setupDefaultValues(ChangeInfo changeInfo, Ref refUsages, Project project) { + return true; + } + + @Override + public void registerConflictResolvers(List snapshots, @NotNull ResolveSnapshotProvider resolveSnapshotProvider, UsageInfo[] usages, ChangeInfo changeInfo) { + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetConstructorParameterTableModel.java b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetConstructorParameterTableModel.java new file mode 100644 index 00000000000..0d8631efa52 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetConstructorParameterTableModel.java @@ -0,0 +1,79 @@ +/* + * 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.openapi.ui.ComboBoxTableRenderer; +import com.intellij.psi.PsiElement; +import com.intellij.refactoring.changeSignature.ParameterTableModelItemBase; +import com.intellij.util.ui.ColumnInfo; +import org.jdesktop.swingx.autocomplete.ComboBoxCellEditor; +import org.jetbrains.jet.plugin.JetFileType; +import org.jetbrains.jet.plugin.refactoring.JetRefactoringBundle; + +import javax.swing.*; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableCellRenderer; + +public class JetConstructorParameterTableModel extends JetFunctionParameterTableModel { + public JetConstructorParameterTableModel(PsiElement context) { + super(context, + new ValVarColumn(), + new NameColumn(context.getProject()), + new TypeColumn(context.getProject(), JetFileType.INSTANCE), + new DefaultValueColumn>(context.getProject(), JetFileType.INSTANCE)); + } + + public static boolean isValVarColumn(ColumnInfo column) { + return column instanceof ValVarColumn; + } + + protected static class ValVarColumn extends ColumnInfoBase, JetValVar> { + public ValVarColumn() { + super(JetRefactoringBundle.message("column.name.val.var")); + } + + @Override + public boolean isCellEditable(ParameterTableModelItemBase item) { + return !item.isEllipsisType() && item.parameter.isNewParameter(); + } + + @Override + public JetValVar valueOf(ParameterTableModelItemBase item) { + return item.parameter.getValOrVar(); + } + + @Override + public void setValue(ParameterTableModelItemBase item, JetValVar value) { + item.parameter.setValOrVar(value); + } + + @Override + public TableCellRenderer doCreateRenderer(ParameterTableModelItemBase item) { + return new ComboBoxTableRenderer(JetValVar.values()); + } + + @Override + public TableCellEditor doCreateEditor(ParameterTableModelItemBase item) { + return new ComboBoxCellEditor(new JComboBox()); + } + + @Override + public int getWidth(JTable table) { + return table.getFontMetrics(table.getFont()).stringWidth(getName()) + 8; + } + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetFunctionParameterTableModel.java b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetFunctionParameterTableModel.java new file mode 100644 index 00000000000..38a6dd6adbb --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetFunctionParameterTableModel.java @@ -0,0 +1,70 @@ +/* + * 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.openapi.project.Project; +import com.intellij.psi.PsiCodeFragment; +import com.intellij.psi.PsiElement; +import com.intellij.refactoring.changeSignature.ParameterTableModelBase; +import com.intellij.refactoring.changeSignature.ParameterTableModelItemBase; +import com.intellij.util.ui.ColumnInfo; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.psi.JetPsiFactory; +import org.jetbrains.jet.plugin.JetFileType; + +public class JetFunctionParameterTableModel extends ParameterTableModelBase> { + private final Project project; + + protected JetFunctionParameterTableModel(PsiElement context, ColumnInfo... columnInfos) { + super(context, context, columnInfos); + project = context.getProject(); + } + + public JetFunctionParameterTableModel(PsiElement context) { + this(context, + new NameColumn(context.getProject()), + new TypeColumn(context.getProject(), JetFileType.INSTANCE), + new DefaultValueColumn>(context.getProject(), JetFileType.INSTANCE)); + } + + @Override + protected ParameterTableModelItemBase createRowItem(@Nullable JetParameterInfo parameterInfo) { + if (parameterInfo == null) { + parameterInfo = new JetParameterInfo(-1); + } + final PsiCodeFragment paramTypeCodeFragment = JetPsiFactory.createTypeCodeFragment(project, parameterInfo.getTypeText(), myTypeContext); + final PsiCodeFragment defaultValueCodeFragment = JetPsiFactory.createExpressionCodeFragment(project, parameterInfo.getDefaultValueText(), myDefaultValueContext); + return new ParameterTableModelItemBase(parameterInfo, paramTypeCodeFragment, defaultValueCodeFragment) { + @Override + public boolean isEllipsisType() { + return false; + } + }; + } + + public static boolean isTypeColumn(ColumnInfo column) { + return column instanceof TypeColumn; + } + + public static boolean isNameColumn(ColumnInfo column) { + return column instanceof NameColumn; + } + + public static boolean isDefaultValueColumn(ColumnInfo column) { + return column instanceof DefaultValueColumn; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetFunctionPlatformDescriptor.java b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetFunctionPlatformDescriptor.java new file mode 100644 index 00000000000..04fabe267e5 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetFunctionPlatformDescriptor.java @@ -0,0 +1,37 @@ +/* + * 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.refactoring.changeSignature.MethodDescriptor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; +import org.jetbrains.jet.lang.descriptors.Visibility; + +public interface JetFunctionPlatformDescriptor extends MethodDescriptor { + boolean isConstructor(); + + @Nullable + String getReturnTypeText(); + + @NotNull + PsiElement getContext(); + + @Nullable + FunctionDescriptor getDescriptor(); +} diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetFunctionPlatformDescriptorImpl.java b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetFunctionPlatformDescriptorImpl.java new file mode 100644 index 00000000000..50385dbe336 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetFunctionPlatformDescriptorImpl.java @@ -0,0 +1,136 @@ +/* + * 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 parameters; + + public JetFunctionPlatformDescriptorImpl(FunctionDescriptor descriptor, PsiElement element) { + funDescriptor = descriptor; + funElement = element; + final List valueParameters = funElement instanceof JetFunction + ? ((JetFunction) funElement).getValueParameters() + : ((JetClass) funElement).getPrimaryConstructorParameters(); + parameters = new ArrayList(ContainerUtil.map(funDescriptor.getValueParameters(), new Function() { + @Override + public JetParameterInfo fun(ValueParameterDescriptor param) { + JetParameter parameter = valueParameters.get(param.getIndex()); + return new JetParameterInfo(param.getIndex(), param.getName().getName(), param.getType(), parameter.getDefaultValue(), parameter.getValOrVarNode()); + } + })); + } + + @Override + public String getName() { + if (funDescriptor instanceof ConstructorDescriptor) + return funDescriptor.getContainingDeclaration().getName().getName(); + else if (funDescriptor instanceof AnonymousFunctionDescriptor) + return ""; + else + return funDescriptor.getName().getName(); + } + + @Override + public List getParameters() { + return parameters; + } + + public void addParameter(JetParameterInfo parameter) { + parameters.add(parameter); + } + + public void removeParameter(int index) { + parameters.remove(index); + } + + @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; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetGeneratedInfo.java b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetGeneratedInfo.java new file mode 100644 index 00000000000..ff4baa41319 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetGeneratedInfo.java @@ -0,0 +1,28 @@ +/* + * 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.PsiFile; + +import java.util.Collections; +import java.util.List; + +public class JetGeneratedInfo { + public List getFilesToWrite() { + return Collections.emptyList(); + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetParameterInfo.java b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetParameterInfo.java new file mode 100644 index 00000000000..55f21bbc1c2 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetParameterInfo.java @@ -0,0 +1,183 @@ +/* + * 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.lang.ASTNode; +import com.intellij.psi.PsiElement; +import com.intellij.refactoring.changeSignature.ParameterInfo; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.psi.JetFunction; +import org.jetbrains.jet.lang.psi.JetParameter; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.name.Name; +import org.jetbrains.jet.lang.resolve.scopes.JetScope; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lexer.JetTokens; +import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache; +import org.jetbrains.jet.renderer.DescriptorRenderer; + +import java.util.List; + +public class JetParameterInfo implements ParameterInfo { + private String name = ""; + private final int oldIndex; + private JetType type; + private String typeText; + private String defaultValueText = ""; + private JetValVar valOrVar; + @Nullable private JetExpression defaultValue; + + public JetParameterInfo(int oldIndex, String name, JetType type, @Nullable JetExpression defaultValue, @Nullable ASTNode valOrVar) { + this.oldIndex = oldIndex; + this.name = name; + this.type = type; + this.typeText = getOldTypeText(); + this.defaultValue = defaultValue; + + if (valOrVar == null) + this.valOrVar = JetValVar.None; + else if (valOrVar.getElementType() == JetTokens.VAL_KEYWORD) + this.valOrVar = JetValVar.Val; + else if (valOrVar.getElementType() == JetTokens.VAR_KEYWORD) + this.valOrVar = JetValVar.Var; + else + throw new IllegalArgumentException("Unknown val/var token: " + valOrVar.getText()); + } + + public JetParameterInfo(String name, JetType type) { + this(-1, name, type, null, null); + } + + public JetParameterInfo(int index) { + oldIndex = index; + typeText = ""; + valOrVar = JetValVar.None; + } + + @Override + public String getName() { + return name; + } + + public String getInheritedName(boolean isInherited, @Nullable PsiElement inheritedFunction, @NotNull JetFunctionPlatformDescriptor baseFunction) { + if (!(inheritedFunction instanceof JetFunction)) + return name; + + List inheritedParameters = ((JetFunction) inheritedFunction).getValueParameters(); + + if (!isInherited || oldIndex < 0 || oldIndex >= baseFunction.getParametersCount() || oldIndex >= inheritedParameters.size()) + return name; + + JetParameterInfo oldParam = baseFunction.getParameters().get(oldIndex); + JetParameter inheritedParam = inheritedParameters.get(oldIndex); + String inheritedParamName = inheritedParam.getName(); + + if (oldParam.getName().equals(inheritedParamName)) { + BindingContext bindingContext = AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) inheritedFunction.getContainingFile()).getBindingContext(); + JetScope parametersScope = JetChangeSignatureUsageProcessor.getFunctionBodyScope((JetFunction) inheritedFunction, bindingContext); + + if (parametersScope != null && parametersScope.getLocalVariable(Name.identifier(name)) == null) + return name; + else + return inheritedParamName; + } + else + return inheritedParamName; + } + + @Override + public int getOldIndex() { + return oldIndex; + } + + public boolean isNewParameter() { + return oldIndex == -1; + } + + @Nullable + @Override + public String getDefaultValue() { + return null; + } + + @Override + public void setName(String name) { + this.name = name != null ? name : ""; + } + + @Override + public boolean isUseAnySingleVariable() { + return false; + } + + @Override + public void setUseAnySingleVariable(boolean b) { + throw new UnsupportedOperationException(); + } + + private String getOldTypeText() { + return DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(type); + } + + @Override + public String getTypeText() { + return typeText; + } + + public void setTypeText(String typeText) { + this.typeText = typeText; + } + + public boolean isTypeChanged() { + return !getOldTypeText().equals(typeText); + } + + public String getDefaultValueText() { + return defaultValueText; + } + + public void setDefaultValueText(String defaultValueText) { + this.defaultValueText = defaultValueText; + } + + public JetValVar getValOrVar() { + return valOrVar != null ? valOrVar : JetValVar.None; + } + + public void setValOrVar(JetValVar valOrVar) { + this.valOrVar = valOrVar; + } + + public String getDeclarationSignature(boolean isInherited, PsiElement inheritedFunction, JetFunctionPlatformDescriptor baseFunction) { + StringBuilder buffer = new StringBuilder(); + JetValVar valVar = getValOrVar(); + + if (valVar != JetValVar.None) + buffer.append(valVar.toString()).append(' '); + + buffer.append(getInheritedName(isInherited, inheritedFunction, baseFunction)); + buffer.append(": ").append(getTypeText()); + + if (defaultValue != null) + buffer.append(" = ").append(defaultValue.getText()); + + return buffer.toString(); + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetUsagesViewDescriptor.java b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetUsagesViewDescriptor.java new file mode 100644 index 00000000000..a723e933b77 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetUsagesViewDescriptor.java @@ -0,0 +1,54 @@ +/* + * 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.refactoring.RefactoringBundle; +import com.intellij.usageView.UsageViewBundle; +import com.intellij.usageView.UsageViewDescriptor; +import org.jetbrains.annotations.NotNull; + +public class JetUsagesViewDescriptor implements UsageViewDescriptor { + private final PsiElement myElement; + private final String myElementsHeader; + + public JetUsagesViewDescriptor(PsiElement element, String elementsHeader) { + myElement = element; + myElementsHeader = elementsHeader; + } + + @NotNull + @Override + public PsiElement[] getElements() { + return myElement != null ? new PsiElement[] {myElement} : new PsiElement[0]; + } + + @Override + public String getProcessedElementsHeader() { + return myElementsHeader; + } + + @Override + public String getCodeReferencesText(int usagesCount, int filesCount) { + return RefactoringBundle.message("references.to.be.changed", UsageViewBundle.getReferencesString(usagesCount, filesCount)); + } + + @Override + public String getCommentReferencesText(int usagesCount, int filesCount) { + return RefactoringBundle.message("comments.elements.header", UsageViewBundle.getOccurencesString(usagesCount, filesCount)); + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetValVar.java b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetValVar.java new file mode 100644 index 00000000000..59cd939e48e --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/JetValVar.java @@ -0,0 +1,37 @@ +/* + * 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; + +public enum JetValVar { + None, + Val, + Var; + + @Override + public String toString() { + switch (this) { + case None: + return "none"; + case Val: + return "val"; + case Var: + return "var"; + } + + return ""; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/usages/JetFunctionCallUsage.java b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/usages/JetFunctionCallUsage.java new file mode 100644 index 00000000000..474f8b74c77 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/usages/JetFunctionCallUsage.java @@ -0,0 +1,98 @@ +/* + * 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.usages; + +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.plugin.refactoring.changeSignature.JetChangeInfo; +import org.jetbrains.jet.plugin.refactoring.changeSignature.JetParameterInfo; + +import java.util.List; + +public class JetFunctionCallUsage extends JetUsageInfo { + private final PsiElement function; + private final boolean isInherited; + + public JetFunctionCallUsage(@NotNull JetCallElement element, @NotNull PsiElement function, boolean isInherited) { + super(element); + this.function = function; + this.isInherited = isInherited; + } + + @Override + public boolean processUsage(JetChangeInfo changeInfo, JetCallElement element) { + JetValueArgumentList arguments = element.getValueArgumentList(); + + if (changeInfo.isNameChanged()) { + JetExpression callee = element.getCalleeExpression(); + + if (callee instanceof JetSimpleNameExpression) + callee.replace(JetPsiFactory.createSimpleName(getProject(), changeInfo.getNewName())); + } + + if (arguments != null) { + List oldArguments = arguments.getArguments(); + + if (changeInfo.isParameterSetOrOrderChanged()) { + StringBuilder parametersBuilder = new StringBuilder("("); + boolean isFirst = true; + + for (JetParameterInfo parameterInfo : changeInfo.getNewParameters()) { + if (isFirst) + isFirst = false; + else + parametersBuilder.append(','); + + String defaultValueText = parameterInfo.getDefaultValueText(); + parametersBuilder.append(defaultValueText.isEmpty() ? '0' : defaultValueText); + } + + parametersBuilder.append(')'); + JetValueArgumentList newArguments = JetPsiFactory.createCallArguments(getProject(), parametersBuilder.toString()); + int argIndex = 0; + + for (JetValueArgument newArgument : newArguments.getArguments()) { + JetParameterInfo parameterInfo = changeInfo.getNewParameters()[argIndex++]; + int oldIndex = parameterInfo.getOldIndex(); + + if (oldIndex >= 0 && oldIndex < oldArguments.size()) + newArgument.replace(oldArguments.get(oldIndex)); + else if (parameterInfo.getDefaultValueText().isEmpty()) + newArgument.delete(); + } + + arguments.replace(newArguments); + } + else { + for (JetParameterInfo parameterInfo : changeInfo.getNewParameters()) { + JetValueArgument argument = parameterInfo.getOldIndex() < oldArguments.size() ? oldArguments.get(parameterInfo.getOldIndex()) : null; + JetValueArgumentName argumentName = argument != null ? argument.getArgumentName() : null; + JetSimpleNameExpression argumentNameExpression = argumentName != null ? argumentName.getReferenceExpression() : null; + PsiElement identifier = argumentNameExpression != null ? argumentNameExpression.getIdentifier() : null; + + if (identifier != null) { + String newName = parameterInfo.getInheritedName(isInherited, function, changeInfo.getFunctionDescriptor()); + identifier.replace(JetPsiFactory.createIdentifier(getProject(), newName)); + } + } + } + } + + return true; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/usages/JetFunctionDefinitionUsage.java b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/usages/JetFunctionDefinitionUsage.java new file mode 100644 index 00000000000..ed14ed63c71 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/usages/JetFunctionDefinitionUsage.java @@ -0,0 +1,148 @@ +/* + * 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.usages; + +import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; +import org.jetbrains.jet.lexer.JetKeywordToken; +import org.jetbrains.jet.lexer.JetTokens; +import org.jetbrains.jet.plugin.intentions.SpecifyTypeExplicitlyAction; +import org.jetbrains.jet.plugin.quickfix.AddModifierFix; +import org.jetbrains.jet.plugin.quickfix.ChangeFunctionReturnTypeFix; +import org.jetbrains.jet.plugin.quickfix.ChangeVisibilityModifierFix; +import org.jetbrains.jet.plugin.refactoring.JetRefactoringUtil; +import org.jetbrains.jet.plugin.refactoring.changeSignature.JetChangeInfo; +import org.jetbrains.jet.plugin.refactoring.changeSignature.JetParameterInfo; +import org.jetbrains.jet.plugin.refactoring.changeSignature.JetValVar; + +public class JetFunctionDefinitionUsage extends JetUsageInfo { + private final boolean isInherited; + + public JetFunctionDefinitionUsage(@NotNull PsiElement function, boolean isInherited) { + super(function); + this.isInherited = isInherited; + } + + @Override + public boolean processUsage(JetChangeInfo changeInfo, PsiElement element) { + JetParameterList parameterList; + + if (element instanceof JetFunction) { + JetFunction function = (JetFunction) element; + parameterList = function.getValueParameterList(); + + if (changeInfo.isNameChanged()) { + PsiElement identifier = function.getNameIdentifier(); + + if (identifier != null) + identifier.replace(JetPsiFactory.createIdentifier(element.getProject(), changeInfo.getNewName())); + } + if (changeInfo.isReturnTypeChanged()) { + SpecifyTypeExplicitlyAction.removeTypeAnnotation(function); + String returnTypeText = changeInfo.getNewReturnTypeText(); + + //TODO use ChangeFunctionReturnTypeFix.invoke when JetTypeCodeFragment.getType() is ready + if (!KotlinBuiltIns.getInstance().getUnitType().toString().equals(returnTypeText)) + ChangeFunctionReturnTypeFix.addReturnTypeAnnotation(getProject(), function, returnTypeText); + } + } + else + parameterList = ((JetClass) element).getPrimaryConstructorParameterList(); + + if (changeInfo.isParameterSetOrOrderChanged()) { + String parametersText = changeInfo.getNewParametersSignature(element, isInherited, 0); + JetParameterList newParameterList = JetPsiFactory.createParameterList(getProject(), parametersText); + + if (parameterList != null) + parameterList = (JetParameterList) parameterList.replace(newParameterList); + else if (element instanceof JetClass) { + PsiElement anchor = ((JetClass) element).getTypeParameterList(); + + if (anchor == null) + anchor = ((JetClass) element).getNameIdentifier(); + if (anchor != null) + parameterList = (JetParameterList) element.addAfter(newParameterList, anchor); + } + } + else if (parameterList != null) { + int paramIndex = 0; + + for (JetParameter parameter : parameterList.getParameters()) { + JetParameterInfo parameterInfo = changeInfo.getNewParameters()[paramIndex++]; + changeParameter(changeInfo, element, parameter, parameterInfo); + } + } + + if (changeInfo.isVisibilityChanged()) + changeVisibility(changeInfo, element, parameterList); + + return true; + } + + private void changeVisibility(JetChangeInfo changeInfo, PsiElement element, JetParameterList parameterList) { + JetKeywordToken newVisibilityToken = JetRefactoringUtil.getVisibilityToken(changeInfo.getNewVisibility()); + + if (element instanceof JetFunction) { + JetModifierList modifierList = newVisibilityToken == JetTokens.INTERNAL_KEYWORD ? null : + JetPsiFactory.createModifierList(getProject(), newVisibilityToken); + AddModifierFix.changeModifier(element, ((JetFunction) element).getModifierList(), null, + ChangeVisibilityModifierFix.VISIBILITY_TOKENS, getProject(), true, modifierList); + } + else { + JetModifierList modifierList = newVisibilityToken == JetTokens.PUBLIC_KEYWORD ? null : + JetPsiFactory.createConstructorModifierList(getProject(), newVisibilityToken); + AddModifierFix.changeModifier(element, ((JetClass) element).getPrimaryConstructorModifierList(), parameterList, + ChangeVisibilityModifierFix.VISIBILITY_TOKENS, getProject(), true, modifierList); + } + } + + private void changeParameter(JetChangeInfo changeInfo, PsiElement element, JetParameter parameter, JetParameterInfo parameterInfo) { + ASTNode valOrVarAstNode = parameter.getValOrVarNode(); + PsiElement valOrVarNode = valOrVarAstNode != null ? valOrVarAstNode.getPsi() : null; + JetValVar valOrVar = parameterInfo.getValOrVar(); + + if (valOrVarNode != null) { + if (valOrVar == JetValVar.None) + valOrVarNode.delete(); + else + valOrVarNode.replace(JetPsiFactory.createValOrVarNode(getProject(), valOrVar.toString()).getPsi()); + } + else if (valOrVar != JetValVar.None) { + PsiElement firstChild = parameter.getFirstChild(); + parameter.addBefore(JetPsiFactory.createValOrVarNode(getProject(), valOrVar.toString()).getPsi(), firstChild); + parameter.addBefore(JetPsiFactory.createWhiteSpace(getProject()), firstChild); + } + + if (parameterInfo.isTypeChanged()) { + JetTypeReference newType = JetPsiFactory.createType(getProject(), parameterInfo.getTypeText()); + JetTypeReference typeReference = parameter.getTypeReference(); + + if (typeReference != null) + typeReference.replace(newType); + } + + PsiElement identifier = parameter.getNameIdentifier(); + + if (identifier != null) { + String newName = parameterInfo.getInheritedName(isInherited, element, changeInfo.getFunctionDescriptor()); + identifier.replace(JetPsiFactory.createIdentifier(parameter.getProject(), newName)); + } + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/usages/JetParameterUsage.java b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/usages/JetParameterUsage.java new file mode 100644 index 00000000000..e6b8f16a55e --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/usages/JetParameterUsage.java @@ -0,0 +1,44 @@ +/* + * 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.usages; + +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetPsiFactory; +import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; +import org.jetbrains.jet.plugin.refactoring.changeSignature.JetChangeInfo; +import org.jetbrains.jet.plugin.refactoring.changeSignature.JetParameterInfo; + +public class JetParameterUsage extends JetUsageInfo { + private final JetParameterInfo parameterInfo; + private final PsiElement function; + private final boolean isInherited; + + public JetParameterUsage(@NotNull JetSimpleNameExpression element, JetParameterInfo parameterInfo, PsiElement function, boolean inherited) { + super(element); + this.parameterInfo = parameterInfo; + this.function = function; + isInherited = inherited; + } + + @Override + public boolean processUsage(JetChangeInfo changeInfo, JetSimpleNameExpression element) { + String newName = parameterInfo.getInheritedName(isInherited, function, changeInfo.getFunctionDescriptor()); + element.replace(JetPsiFactory.createSimpleName(element.getProject(), newName)); + return false; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/usages/JetUsageInfo.java b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/usages/JetUsageInfo.java new file mode 100644 index 00000000000..b2fb58c6c39 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/changeSignature/usages/JetUsageInfo.java @@ -0,0 +1,35 @@ +/* + * 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.usages; + +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.usageView.UsageInfo; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.plugin.refactoring.changeSignature.JetChangeInfo; + +public abstract class JetUsageInfo extends UsageInfo { + public JetUsageInfo(@NotNull T element) { + super(element); + } + + public JetUsageInfo(@NotNull PsiReference reference) { + super(reference); + } + + public abstract boolean processUsage(JetChangeInfo changeInfo, T element); +} diff --git a/idea/testData/refactoring/changeSignature/AddConstructorVisibilityAfter.kt b/idea/testData/refactoring/changeSignature/AddConstructorVisibilityAfter.kt new file mode 100644 index 00000000000..c189c633905 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/AddConstructorVisibilityAfter.kt @@ -0,0 +1,5 @@ +class C1 protected (val x: Any){} + +fun f() { + val c = C1(12); +} diff --git a/idea/testData/refactoring/changeSignature/AddConstructorVisibilityBefore.kt b/idea/testData/refactoring/changeSignature/AddConstructorVisibilityBefore.kt new file mode 100644 index 00000000000..0c95fb13036 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/AddConstructorVisibilityBefore.kt @@ -0,0 +1,5 @@ +class C1{} + +fun f() { + val c = C1(); +} diff --git a/idea/testData/refactoring/changeSignature/AddReturnTypeAfter.kt b/idea/testData/refactoring/changeSignature/AddReturnTypeAfter.kt new file mode 100644 index 00000000000..1938d50a11c --- /dev/null +++ b/idea/testData/refactoring/changeSignature/AddReturnTypeAfter.kt @@ -0,0 +1,3 @@ +fun x(a: Int): Float { + x(1); +} diff --git a/idea/testData/refactoring/changeSignature/AddReturnTypeBefore.kt b/idea/testData/refactoring/changeSignature/AddReturnTypeBefore.kt new file mode 100644 index 00000000000..efbe60a8024 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/AddReturnTypeBefore.kt @@ -0,0 +1,3 @@ +fun x(a: Int) { + x(1); +} diff --git a/idea/testData/refactoring/changeSignature/BadSelectionBefore.kt b/idea/testData/refactoring/changeSignature/BadSelectionBefore.kt new file mode 100644 index 00000000000..06a0448ad5f --- /dev/null +++ b/idea/testData/refactoring/changeSignature/BadSelectionBefore.kt @@ -0,0 +1,3 @@ +fun x(a: Int): Float { + x(1); +} diff --git a/idea/testData/refactoring/changeSignature/ChangeConstructorVisibilityAfter.kt b/idea/testData/refactoring/changeSignature/ChangeConstructorVisibilityAfter.kt new file mode 100644 index 00000000000..542c05bc799 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/ChangeConstructorVisibilityAfter.kt @@ -0,0 +1 @@ +class C1 protected (){} diff --git a/idea/testData/refactoring/changeSignature/ChangeConstructorVisibilityBefore.kt b/idea/testData/refactoring/changeSignature/ChangeConstructorVisibilityBefore.kt new file mode 100644 index 00000000000..d414a8ec373 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/ChangeConstructorVisibilityBefore.kt @@ -0,0 +1 @@ +class C1(){} diff --git a/idea/testData/refactoring/changeSignature/ChangeReturnTypeAfter.kt b/idea/testData/refactoring/changeSignature/ChangeReturnTypeAfter.kt new file mode 100644 index 00000000000..1938d50a11c --- /dev/null +++ b/idea/testData/refactoring/changeSignature/ChangeReturnTypeAfter.kt @@ -0,0 +1,3 @@ +fun x(a: Int): Float { + x(1); +} diff --git a/idea/testData/refactoring/changeSignature/ChangeReturnTypeBefore.kt b/idea/testData/refactoring/changeSignature/ChangeReturnTypeBefore.kt new file mode 100644 index 00000000000..416c32a0305 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/ChangeReturnTypeBefore.kt @@ -0,0 +1,3 @@ +fun x(a: Int): Int { + x(1); +} diff --git a/idea/testData/refactoring/changeSignature/ConstructorAfter.kt b/idea/testData/refactoring/changeSignature/ConstructorAfter.kt new file mode 100644 index 00000000000..7a7c772aaff --- /dev/null +++ b/idea/testData/refactoring/changeSignature/ConstructorAfter.kt @@ -0,0 +1,14 @@ +open class C0(val x: Any?) {} + +open class C1 (var _x1: Int = 1, _x2: Float?, val _x3: ((Int) -> Int)?) : C0(_x3){ + fun bar() { + val y1 = _x1; + val y2 = _x2; + } +} +class C2 : C1(1, 2.5, null) { + fun foo() { + var c = C1(2, 3.5, null); + c = C1(_x1 = 2, _x2 = 3.5, _x3 = null); + } +} diff --git a/idea/testData/refactoring/changeSignature/ConstructorBefore.kt b/idea/testData/refactoring/changeSignature/ConstructorBefore.kt new file mode 100644 index 00000000000..f115040e235 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/ConstructorBefore.kt @@ -0,0 +1,14 @@ +open class C0(val x: Any?) {} + +open class C1 protected (val x1: Int = 1, var x2: Float, x3: ((Int) -> Int)?) : C0(x3){ + fun bar() { + val y1 = x1; + val y2 = x2; + } +} +class C2 : C1(1, 2.5, null) { + fun foo() { + var c = C1(2, 3.5, null); + c = C1(x1 = 2, x2 = 3.5, x3 = null); + } +} diff --git a/idea/testData/refactoring/changeSignature/ConstructorSwapArgumentsAfter.kt b/idea/testData/refactoring/changeSignature/ConstructorSwapArgumentsAfter.kt new file mode 100644 index 00000000000..3dd24059fe7 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/ConstructorSwapArgumentsAfter.kt @@ -0,0 +1,14 @@ +open class C1 protected (x3: ((Int) -> Int)?, + var _x2: Float, + val _x1: Int = 1){ + fun bar() { + val y1 = _x1; + val y2 = _x2; + } +} +class C2 : C1(null, 2.5, 1) { + fun foo() { + var c = C1(null, 3.5, 2); + c = C1(x3 = null, _x2 = 3.5, _x1 = 2); + } +} diff --git a/idea/testData/refactoring/changeSignature/ConstructorSwapArgumentsBefore.kt b/idea/testData/refactoring/changeSignature/ConstructorSwapArgumentsBefore.kt new file mode 100644 index 00000000000..f18ff2adeb7 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/ConstructorSwapArgumentsBefore.kt @@ -0,0 +1,12 @@ +open class C1 protected (val x1: Int = 1, var x2: Float, x3: ((Int) -> Int)?){ + fun bar() { + val y1 = x1; + val y2 = x2; + } +} +class C2 : C1(1, 2.5, null) { + fun foo() { + var c = C1(2, 3.5, null); + c = C1(x1 = 2, x2 = 3.5, x3 = null); + } +} diff --git a/idea/testData/refactoring/changeSignature/ConstructorsConflictBefore.kt b/idea/testData/refactoring/changeSignature/ConstructorsConflictBefore.kt new file mode 100644 index 00000000000..8c7124abf2b --- /dev/null +++ b/idea/testData/refactoring/changeSignature/ConstructorsConflictBefore.kt @@ -0,0 +1,5 @@ +open class Class(x: Int, val y: Int, val z: Int, _y: Int, var i: Int) { + var _x: Int = 0; + var _y: Int = 0; + var _z: Int = 0; +} diff --git a/idea/testData/refactoring/changeSignature/ConstructorsConflictMessages.txt b/idea/testData/refactoring/changeSignature/ConstructorsConflictMessages.txt new file mode 100644 index 00000000000..16607cbaf2b --- /dev/null +++ b/idea/testData/refactoring/changeSignature/ConstructorsConflictMessages.txt @@ -0,0 +1,3 @@ +Duplicating parameter '_y' +Duplicating property '_y' +Duplicating property '_z' \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/ExpressionFunctionAfter.kt b/idea/testData/refactoring/changeSignature/ExpressionFunctionAfter.kt new file mode 100644 index 00000000000..6891478720a --- /dev/null +++ b/idea/testData/refactoring/changeSignature/ExpressionFunctionAfter.kt @@ -0,0 +1,2 @@ +fun fun1(x1: Int, + y1: Int): Int = x1 * 2 + fun1(x1, ) diff --git a/idea/testData/refactoring/changeSignature/ExpressionFunctionBefore.kt b/idea/testData/refactoring/changeSignature/ExpressionFunctionBefore.kt new file mode 100644 index 00000000000..602c8447c4f --- /dev/null +++ b/idea/testData/refactoring/changeSignature/ExpressionFunctionBefore.kt @@ -0,0 +1 @@ +fun fun1(x : Int) : Int = x * 2 + fun1(x) diff --git a/idea/testData/refactoring/changeSignature/FunctionLiteralAfter.kt b/idea/testData/refactoring/changeSignature/FunctionLiteralAfter.kt new file mode 100644 index 00000000000..376f9e6f3f1 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/FunctionLiteralAfter.kt @@ -0,0 +1,5 @@ +fun foo() { + val v1 = {(z: Int, + y1: String, + x: Any): Int -> println(z); println(y1) } +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/FunctionLiteralBefore.kt b/idea/testData/refactoring/changeSignature/FunctionLiteralBefore.kt new file mode 100644 index 00000000000..f027cbe8f3c --- /dev/null +++ b/idea/testData/refactoring/changeSignature/FunctionLiteralBefore.kt @@ -0,0 +1,3 @@ +fun foo() { + val v1 = {(z: Int, y: String) -> println(z); println(y) } +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/FunctionsAddRemoveArgumentsAfter.kt b/idea/testData/refactoring/changeSignature/FunctionsAddRemoveArgumentsAfter.kt new file mode 100644 index 00000000000..e80516bcaf8 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/FunctionsAddRemoveArgumentsAfter.kt @@ -0,0 +1,12 @@ +fun foo(x0: Any?, + x1: Int = 1, + x2: Float) { + foo(null, 2, 3.5); + val y1 = x1; + val y2 = x2; + val y3 = x3; +} + +fun bar() { + foo(null, x1 = 2, x2 = 3.5); +} diff --git a/idea/testData/refactoring/changeSignature/FunctionsAddRemoveArgumentsBefore.kt b/idea/testData/refactoring/changeSignature/FunctionsAddRemoveArgumentsBefore.kt new file mode 100644 index 00000000000..e730677d332 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/FunctionsAddRemoveArgumentsBefore.kt @@ -0,0 +1,10 @@ +protected fun foo(x1: Int = 1, x2: Float, x3: ((Int) -> Int)?) { + foo(2, 3.5, null); + val y1 = x1; + val y2 = x2; + val y3 = x3; +} + +fun bar() { + foo(x1 = 2, x2 = 3.5, x3 = null); +} diff --git a/idea/testData/refactoring/changeSignature/FunctionsAfter.kt b/idea/testData/refactoring/changeSignature/FunctionsAfter.kt new file mode 100644 index 00000000000..806feff5dea --- /dev/null +++ b/idea/testData/refactoring/changeSignature/FunctionsAfter.kt @@ -0,0 +1,10 @@ +public fun foo(_x1: Int = 1, _x2: Float?, _x3: ((Int) -> Int)?) { + foo(2, 3.5, null); + val y1 = _x1; + val y2 = _x2; + val y3 = _x3; +} + +fun bar() { + foo(_x1 = 2, _x2 = 3.5, _x3 = null); +} diff --git a/idea/testData/refactoring/changeSignature/FunctionsBefore.kt b/idea/testData/refactoring/changeSignature/FunctionsBefore.kt new file mode 100644 index 00000000000..ef266d93854 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/FunctionsBefore.kt @@ -0,0 +1,10 @@ +fun foo(x1: Int = 1, x2: Float, x3: ((Int) -> Int)?) { + foo(2, 3.5, null); + val y1 = x1; + val y2 = x2; + val y3 = x3; +} + +fun bar() { + foo(x1 = 2, x2 = 3.5, x3 = null); +} diff --git a/idea/testData/refactoring/changeSignature/InnerFunctionsConflictBefore.kt b/idea/testData/refactoring/changeSignature/InnerFunctionsConflictBefore.kt new file mode 100644 index 00000000000..d56b4f188e7 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/InnerFunctionsConflictBefore.kt @@ -0,0 +1,4 @@ +fun outer() { + fun inner1(x: Int, y: Int): Int { val y: Int; } + fun inner2(x: Int, y: Int): Any { } +} diff --git a/idea/testData/refactoring/changeSignature/InnerFunctionsConflictMessages.txt b/idea/testData/refactoring/changeSignature/InnerFunctionsConflictMessages.txt new file mode 100644 index 00000000000..4b7eb61a5cf --- /dev/null +++ b/idea/testData/refactoring/changeSignature/InnerFunctionsConflictMessages.txt @@ -0,0 +1,3 @@ +Duplicating local variable 'y' +Duplicating parameter 'y' +Function already exists: 'local final fun inner2(x : Int, y : Int) : Any defined in outer' \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/MemberFunctionsConflictBefore.kt b/idea/testData/refactoring/changeSignature/MemberFunctionsConflictBefore.kt new file mode 100644 index 00000000000..f9fa3e52e36 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/MemberFunctionsConflictBefore.kt @@ -0,0 +1,4 @@ +class outer() { + fun inner1(x: Int, z: Int) { fun inner3() { val y: Int; }} + fun inner2(x: Int, y: Int) { } +} diff --git a/idea/testData/refactoring/changeSignature/MemberFunctionsConflictMessages.txt b/idea/testData/refactoring/changeSignature/MemberFunctionsConflictMessages.txt new file mode 100644 index 00000000000..203eae788f3 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/MemberFunctionsConflictMessages.txt @@ -0,0 +1 @@ +Function already exists: 'internal final fun inner2(x : Int, y : Int) : Unit defined in outer' \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/RemoveReturnTypeAfter.kt b/idea/testData/refactoring/changeSignature/RemoveReturnTypeAfter.kt new file mode 100644 index 00000000000..1873061ec76 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/RemoveReturnTypeAfter.kt @@ -0,0 +1,3 @@ +fun x(a: Int) { + x(1); +} diff --git a/idea/testData/refactoring/changeSignature/RemoveReturnTypeBefore.kt b/idea/testData/refactoring/changeSignature/RemoveReturnTypeBefore.kt new file mode 100644 index 00000000000..9cd34000213 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/RemoveReturnTypeBefore.kt @@ -0,0 +1,3 @@ +fun x(a: Int): Float { + x(1); +} diff --git a/idea/testData/refactoring/changeSignature/RenameFunctionAfter.kt b/idea/testData/refactoring/changeSignature/RenameFunctionAfter.kt new file mode 100644 index 00000000000..09d22d6d176 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/RenameFunctionAfter.kt @@ -0,0 +1,6 @@ +fun after(a: Int) { + after(1); +} + +fun after(a: Int, b: Int) { +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/RenameFunctionBefore.kt b/idea/testData/refactoring/changeSignature/RenameFunctionBefore.kt new file mode 100644 index 00000000000..673a889aeac --- /dev/null +++ b/idea/testData/refactoring/changeSignature/RenameFunctionBefore.kt @@ -0,0 +1,6 @@ +fun x(a: Int) { + x(1); +} + +fun after(a: Int, b: Int) { +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/TopLevelFunctionsConflictBefore.kt b/idea/testData/refactoring/changeSignature/TopLevelFunctionsConflictBefore.kt new file mode 100644 index 00000000000..d5ce7c93e86 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/TopLevelFunctionsConflictBefore.kt @@ -0,0 +1,2 @@ +fun fun1() { } +fun fun2() { } diff --git a/idea/testData/refactoring/changeSignature/TopLevelFunctionsConflictMessages.txt b/idea/testData/refactoring/changeSignature/TopLevelFunctionsConflictMessages.txt new file mode 100644 index 00000000000..2d3a2537051 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/TopLevelFunctionsConflictMessages.txt @@ -0,0 +1 @@ +Function already exists: 'internal fun fun2() : Unit defined in root package' \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/VarargsBefore.kt b/idea/testData/refactoring/changeSignature/VarargsBefore.kt new file mode 100644 index 00000000000..8dc001d9fb0 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/VarargsBefore.kt @@ -0,0 +1,2 @@ +fun foo(x1: Int = 1, vararg x2: Float, x3: ((Int) -> Int)?) { +} diff --git a/idea/tests/org/jetbrains/jet/plugin/refactoring/changeSignature/JetChangeSignatureTest.java b/idea/tests/org/jetbrains/jet/plugin/refactoring/changeSignature/JetChangeSignatureTest.java new file mode 100644 index 00000000000..033479b9a72 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/plugin/refactoring/changeSignature/JetChangeSignatureTest.java @@ -0,0 +1,225 @@ +/* + * 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.openapi.editor.Editor; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +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.Visibilities; +import org.jetbrains.jet.lang.psi.JetPsiFactory; +import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; +import org.jetbrains.jet.plugin.PluginTestCaseBase; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class JetChangeSignatureTest extends LightCodeInsightTestCase { + public void testBadSelection() throws Exception { + configureByFile(getTestName(false) + "Before.kt"); + Editor editor = getEditor(); + PsiFile file = getFile(); + assertNull(new JetChangeSignatureHandler().findTargetMember(file, editor)); + } + + public void testRenameFunction() throws Exception { + JetChangeInfo changeInfo = getChangeInfo(); + changeInfo.setNewName("after"); + doTest(changeInfo); + } + + public void testChangeReturnType() throws Exception { + JetChangeInfo changeInfo = getChangeInfo(); + changeInfo.setNewReturnTypeText("Float"); + doTest(changeInfo); + } + + public void testAddReturnType() throws Exception { + JetChangeInfo changeInfo = getChangeInfo(); + changeInfo.setNewReturnTypeText("Float"); + doTest(changeInfo); + } + + public void testRemoveReturnType() throws Exception { + JetChangeInfo changeInfo = getChangeInfo(); + changeInfo.setNewReturnTypeText("Unit"); + doTest(changeInfo); + } + + public void testChangeConstructorVisibility() throws Exception { + JetChangeInfo changeInfo = getChangeInfo(); + changeInfo.setNewVisibility(Visibilities.PROTECTED); + doTest(changeInfo); + } + + public void testAddConstructorVisibility() throws Exception { + JetChangeInfo changeInfo = getChangeInfo(); + changeInfo.setNewVisibility(Visibilities.PROTECTED); + JetParameterInfo newParameter = new JetParameterInfo(-1, "x", KotlinBuiltIns.getInstance().getAnyType(), + null, JetPsiFactory.createValOrVarNode(getProject(), "val")); + newParameter.setDefaultValueText("12"); + changeInfo.addParameter(newParameter); + doTest(changeInfo); + } + + public void testConstructor() throws Exception { + JetChangeInfo changeInfo = getChangeInfo(); + changeInfo.setNewVisibility(Visibilities.PUBLIC); + changeInfo.getNewParameters()[0].setValOrVar(JetValVar.Var); + changeInfo.getNewParameters()[1].setValOrVar(JetValVar.None); + changeInfo.getNewParameters()[2].setValOrVar(JetValVar.Val); + changeInfo.getNewParameters()[0].setName("_x1"); + changeInfo.getNewParameters()[1].setName("_x2"); + changeInfo.getNewParameters()[2].setName("_x3"); + changeInfo.getNewParameters()[1].setTypeText("Float?"); + doTest(changeInfo); + } + + public void testConstructorSwapArguments() throws Exception { + JetChangeInfo changeInfo = getChangeInfo(); + changeInfo.getNewParameters()[0].setName("_x1"); + changeInfo.getNewParameters()[1].setName("_x2"); + JetParameterInfo param = changeInfo.getNewParameters()[0]; + changeInfo.setNewParameter(0, changeInfo.getNewParameters()[2]); + changeInfo.setNewParameter(2, param); + doTest(changeInfo); + } + + public void testFunctions() throws Exception { + JetChangeInfo changeInfo = getChangeInfo(); + changeInfo.setNewVisibility(Visibilities.PUBLIC); + changeInfo.getNewParameters()[0].setName("_x1"); + changeInfo.getNewParameters()[1].setName("_x2"); + changeInfo.getNewParameters()[2].setName("_x3"); + changeInfo.getNewParameters()[1].setTypeText("Float?"); + doTest(changeInfo); + } + + public void testExpressionFunction() throws Exception { + JetChangeInfo changeInfo = getChangeInfo(); + changeInfo.getNewParameters()[0].setName("x1"); + changeInfo.addParameter(new JetParameterInfo("y1", KotlinBuiltIns.getInstance().getIntType())); + doTest(changeInfo); + } + + public void testFunctionsAddRemoveArguments() throws Exception { + JetChangeInfo changeInfo = getChangeInfo(); + changeInfo.setNewVisibility(Visibilities.INTERNAL); + changeInfo.setNewParameter(2, changeInfo.getNewParameters()[1]); + changeInfo.setNewParameter(1, changeInfo.getNewParameters()[0]); + JetParameterInfo newParameter = new JetParameterInfo("x0", KotlinBuiltIns.getInstance().getNullableAnyType()); + newParameter.setDefaultValueText("null"); + changeInfo.setNewParameter(0, newParameter); + doTest(changeInfo); + } + + public void testFunctionLiteral() throws Exception { + JetChangeInfo changeInfo = getChangeInfo(); + changeInfo.getNewParameters()[1].setName("y1"); + changeInfo.addParameter(new JetParameterInfo("x", KotlinBuiltIns.getInstance().getAnyType())); + changeInfo.setNewReturnTypeText("Int"); + doTest(changeInfo); + } + + public void testVarargs() throws Exception { + try { + getChangeInfo(); + } + catch (CommonRefactoringUtil.RefactoringErrorHintException e) { + assertEquals("Can't refactor the function with variable arguments", e.getMessage()); + return; + } + + fail("Exception expected"); + } + + public void testInnerFunctionsConflict() throws Exception { + JetChangeInfo changeInfo = getChangeInfo(); + changeInfo.setNewName("inner2"); + changeInfo.getNewParameters()[0].setName("y"); + doTestConflict(changeInfo); + } + + public void testMemberFunctionsConflict() throws Exception { + JetChangeInfo changeInfo = getChangeInfo(); + changeInfo.setNewName("inner2"); + changeInfo.getNewParameters()[0].setName("y"); + doTestConflict(changeInfo); + } + + public void testTopLevelFunctionsConflict() throws Exception { + JetChangeInfo changeInfo = getChangeInfo(); + changeInfo.setNewName("fun2"); + doTestConflict(changeInfo); + } + + public void testConstructorsConflict() throws Exception { + JetChangeInfo changeInfo = getChangeInfo(); + changeInfo.getNewParameters()[0].setName("_x"); + changeInfo.getNewParameters()[1].setName("_y"); + changeInfo.getNewParameters()[2].setName("_z"); + doTestConflict(changeInfo); + } + + @NotNull + @Override + protected String getTestDataPath() { + return new File(PluginTestCaseBase.getTestDataPathBase(), "/refactoring/changeSignature").getPath() + File.separator; + } + + private JetChangeInfo getChangeInfo() throws Exception { + configureByFile(getTestName(false) + "Before.kt"); + Editor editor = getEditor(); + 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); + assertNotNull(dialog); + dialog.canRun(); + return dialog.evaluateChangeInfo(); + } + + private void doTest(JetChangeInfo changeInfo) throws Exception { + new JetChangeSignatureProcessor(getProject(), changeInfo, "Change signature").run(); + checkResultByFile(getTestName(false) + "After.kt"); + } + + private void doTestConflict(JetChangeInfo changeInfo) throws Exception { + try { + new JetChangeSignatureProcessor(getProject(), changeInfo, "Change signature").run(); + } + catch (BaseRefactoringProcessor.ConflictsInTestsException e) { + List messages = new ArrayList(e.getMessages()); + Collections.sort(messages); + File conflictsFile = new File(getTestDataPath() + getTestName(false) + "Messages.txt"); + String fileText = FileUtil.loadFile(conflictsFile, CharsetToolkit.UTF8); + assertEquals(fileText, StringUtil.join(messages, "\n")); + return; + } + + fail("No conflicts found"); + } +}