diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index 221d32a9bb20..61a374a1c9c6 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -1086,6 +1086,11 @@ + + + + + {0} cannot be variable.is.accessed.for.writing=Variable ''{0}'' is accessed for writing introduce.closure.parameter.elements.header=Adding parameter to a closure extract.closure.title=Extract closure +cannot.process.usage.in.language.{0}=Cannot process usage in {0} +you.cannot.pass.as.parameter.0.because.you.remove.1.from.base.method=You cannot pass ''{0}'' as parameter because you remove ''{1}'' from the method. You should deselect "Remove parameter ''{0}'' no longer used" or deselect parameter ''{1}'' in the parameter table. diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/ExtractHandlerBase.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/ExtractHandlerBase.java index 3f6a56b4b6e6..44060186614d 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/ExtractHandlerBase.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/ExtractHandlerBase.java @@ -34,9 +34,13 @@ import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrIfStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrReturnStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrIndexProperty; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrMemberOwner; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner; @@ -45,15 +49,17 @@ import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.ReachingDefin import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.VariableInfo; import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle; import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil; +import org.jetbrains.plugins.groovy.refactoring.inline.GroovyInlineMethodUtil; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Set; /** * @author Max Medvedev */ -public abstract class ExtractHandlerBase implements RefactoringActionHandler { +public abstract class ExtractHandlerBase implements RefactoringActionHandler { private static final Logger LOG = Logger.getInstance(ExtractHandlerBase.class); public void invokeOnEditor(Project project, Editor editor, PsiFile file, int start, int end) throws ExtractException { @@ -68,12 +74,12 @@ public abstract class ExtractHandlerBase i SelectionModel selectionModel = editor.getSelectionModel(); PsiDocumentManager.getInstance(project).commitAllDocuments(); - PsiElement[] elements = ExtractUtil.getElementsInOffset(file, start, end); + PsiElement[] elements = getElementsInOffset(file, start, end); if (elements.length == 1 && elements[0] instanceof GrExpression) { selectionModel.setSelection(start, elements[0].getTextRange().getEndOffset()); } - GrStatement[] statements = ExtractUtil.getStatementsByElements(elements); + GrStatement[] statements = getStatementsByElements(elements); if (statements.length == 0) { String message = @@ -90,8 +96,8 @@ public abstract class ExtractHandlerBase i } GrStatement statement0 = statements[0]; - GrMemberOwner owner = ExtractUtil.getMemberOwner(statement0); - GrStatementOwner declarationOwner = ExtractUtil.getDeclarationOwner(statement0); + GrMemberOwner owner = getMemberOwner(statement0); + GrStatementOwner declarationOwner = getDeclarationOwner(statement0); if (owner == null || declarationOwner == null && !ExtractUtil.isSingleExpression(statements)) { String message = RefactoringBundle.getCannotRefactorMessage(GroovyRefactoringBundle.message("refactoring.is.not.supported.in.the.current.context")); @@ -145,7 +151,7 @@ public abstract class ExtractHandlerBase i boolean hasReturns = returnStatements.size() > 0; List returnStatementsCopy = new ArrayList(returnStatements.size()); returnStatementsCopy.addAll(returnStatements); - boolean isReturnStatement = ExtractUtil.isReturnStatement(statements[statements.length - 1], returnStatementsCopy); + boolean isReturnStatement = isReturnStatement(statements[statements.length - 1], returnStatementsCopy); boolean isLastStatementOfMethod = isLastStatementOfMethodOrClosure(statements); if (hasReturns && !isLastStatementOfMethod && !isReturnStatement || hasInterruptingStatements) { String message = GroovyRefactoringBundle.message("refactoring.is.not.supported.when.return.statement.interrupts.the.execution.flow"); @@ -154,10 +160,8 @@ public abstract class ExtractHandlerBase i InitialInfo info = new InitialInfo(inputInfos, outputInfos, elements, statements, owner, returnStatements); - InfoHelper helper = getSettings(info); - if (helper == null) return; - performRefactoring(helper, owner, declarationOwner, editor, statement0); + performRefactoring(info, owner, declarationOwner, editor, statement0); } private static boolean isLastStatementOfMethodOrClosure(GrStatement[] statements) { @@ -175,12 +179,64 @@ public abstract class ExtractHandlerBase i return statement0.getManager().areElementsEquivalent(lastFromBlock, lastStatement); } - @Nullable - public abstract InfoHelper getSettings(@NotNull final InitialInfo initialInfo); - - public abstract void performRefactoring(@NotNull final InfoHelper helper, + public abstract void performRefactoring(@NotNull final InitialInfo initialInfo, @NotNull final GrMemberOwner owner, final GrStatementOwner declarationOwner, final Editor editor, final PsiElement startElement); + + private static GrStatement[] getStatementsByElements(PsiElement[] elements) { + ArrayList statementList = new ArrayList(); + for (PsiElement element : elements) { + if (element instanceof GrStatement) { + statementList.add(((GrStatement) element)); + } + } + return statementList.toArray(new GrStatement[statementList.size()]); + } + + private static PsiElement[] getElementsInOffset(PsiFile file, int startOffset, int endOffset) { + PsiElement[] elements; + GrExpression expr = GroovyRefactoringUtil.findElementInRange(file, startOffset, endOffset, GrExpression.class); + + if (expr != null) { + PsiElement parent = expr.getParent(); + if (expr.getParent() instanceof GrMethodCallExpression || parent instanceof GrIndexProperty) { + expr = ((GrExpression) expr.getParent()); + } + elements = new PsiElement[]{expr}; + } else { + elements = GroovyRefactoringUtil.findStatementsInRange(file, startOffset, endOffset, true); + } + return elements; + } + + @Nullable + private static GrMemberOwner getMemberOwner(GrStatement statement) { + PsiElement parent = statement.getParent(); + while (parent != null && !(parent instanceof GrMemberOwner)) { + if (parent instanceof GroovyFileBase) return (GrMemberOwner) ((GroovyFileBase) parent).getScriptClass(); + parent = parent.getParent(); + } + return parent != null ? ((GrMemberOwner) parent) : null; + } + + @Nullable + private static GrStatementOwner getDeclarationOwner(GrStatement statement) { + PsiElement parent = statement.getParent(); + return parent instanceof GrStatementOwner ? ((GrStatementOwner) parent) : null; + } + + private static boolean isReturnStatement(GrStatement statement, Collection returnStatements) { + if (statement instanceof GrReturnStatement) return true; + if (statement instanceof GrIfStatement) { + boolean checked = GroovyInlineMethodUtil.checkTailIfStatement(((GrIfStatement)statement), returnStatements); + return checked & returnStatements.size() == 0; + + } + if (statement instanceof GrExpression) { + return returnStatements.contains(statement); + } + return false; + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/ExtractInfoHelper.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/ExtractInfoHelper.java index 2da2a3ddc24c..dca341c1c16e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/ExtractInfoHelper.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/ExtractInfoHelper.java @@ -20,7 +20,6 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiType; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrMemberOwner; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.VariableInfo; /** @@ -34,7 +33,7 @@ public interface ExtractInfoHelper { ParameterInfo[] getParameterInfos(); @NotNull - VariableInfo[] getOutputNames(); + VariableInfo[] getOutputVariableInfos(); @NotNull String[] getArgumentNames(); @@ -48,6 +47,7 @@ public interface ExtractInfoHelper { @NotNull GrStatement[] getStatements(); - @NotNull - GrMemberOwner getOwner(); + boolean hasReturnValue(); + + String getName(); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/ExtractInfoHelperBase.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/ExtractInfoHelperBase.java index c253ceb53596..bc2bfc7e8a90 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/ExtractInfoHelperBase.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/ExtractInfoHelperBase.java @@ -20,7 +20,6 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiType; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrMemberOwner; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.VariableInfo; import java.util.Collection; @@ -30,7 +29,7 @@ import java.util.Map; /** * @author Max Medvedev */ -public class ExtractInfoHelperBase implements ExtractInfoHelper { +public abstract class ExtractInfoHelperBase implements ExtractInfoHelper { protected final InitialInfo myInitialInfo; protected final Map myInputNamesMap; @@ -66,8 +65,8 @@ public class ExtractInfoHelperBase implements ExtractInfoHelper { @Override @NotNull - public VariableInfo[] getOutputNames() { - return myInitialInfo.getOutputNames(); + public VariableInfo[] getOutputVariableInfos() { + return myInitialInfo.getOutputVariableInfos(); } /** @@ -107,12 +106,6 @@ public class ExtractInfoHelperBase implements ExtractInfoHelper { return myInitialInfo.getStatements(); } - @Override - @NotNull - public GrMemberOwner getOwner() { - return myInitialInfo.getOwner(); - } - public boolean hasReturnValue() { return myInitialInfo.hasReturnValue(); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/ExtractUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/ExtractUtil.java index 26db5d4ee16e..309578d656a4 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/ExtractUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/ExtractUtil.java @@ -16,46 +16,33 @@ package org.jetbrains.plugins.groovy.refactoring.extract; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.psi.*; -import com.intellij.refactoring.ui.ConflictsDialog; -import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.HashMap; -import gnu.trove.TObjectHashingStrategy; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.groovy.intentions.utils.DuplicatesUtil; -import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; -import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase; -import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; +import org.jetbrains.plugins.groovy.lang.GrReferenceAdjuster; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrIfStatement; +import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrReturnStatement; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrIndexProperty; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrMemberOwner; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner; import org.jetbrains.plugins.groovy.lang.psi.api.util.GrVariableDeclarationOwner; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.VariableInfo; +import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; -import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass; import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; -import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle; import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil; import org.jetbrains.plugins.groovy.refactoring.extract.method.ExtractMethodInfoHelper; -import org.jetbrains.plugins.groovy.refactoring.inline.GroovyInlineMethodUtil; import java.util.*; @@ -63,44 +50,45 @@ import java.util.*; * @author ilyas */ public class ExtractUtil { + private static final Logger LOG = Logger.getInstance(ExtractUtil.class); private ExtractUtil() { } - @Nullable - public static PsiElement calculateAnchorToInsertBefore(GrMemberOwner owner, PsiElement startElement) { - while (startElement != null && !isEnclosingDefinition(owner, startElement)) { - if (startElement.getParent() instanceof GroovyFile) { - return startElement.getNextSibling(); - } - startElement = startElement.getParent(); - PsiElement parent = startElement.getParent(); - if (parent instanceof GroovyFile && ((GroovyFile) parent).getScriptClass() == owner) { - return startElement.getNextSibling(); + public static GrStatement replaceStatement(GrStatementOwner declarationOwner, ExtractInfoHelper helper) { + GrStatement realStatement; + if (declarationOwner != null && !isSingleExpression(helper.getStatements())) { + // Replace set of statements + final GrStatement[] newStatement = createResultStatement(helper); + // add call statement + final GrStatement[] statements = helper.getStatements(); + LOG.assertTrue(statements.length > 0); + realStatement = null; + for (GrStatement statement : newStatement) { + realStatement = declarationOwner.addStatementBefore(statement, statements[0]); } + LOG.assertTrue(realStatement != null); + // remove old statements + removeOldStatements(declarationOwner, helper); + PsiImplUtil.removeNewLineAfter(realStatement); } - return startElement == null ? null : startElement.getNextSibling(); - } - - private static boolean isEnclosingDefinition(GrMemberOwner owner, PsiElement startElement) { - if (owner instanceof GrTypeDefinition) { - GrTypeDefinition definition = (GrTypeDefinition) owner; - return startElement.getParent() == definition.getBody(); + else { + // Expression call replace + GrExpression methodCall = createMethodCall(helper); + GrExpression oldExpr = (GrExpression)helper.getStatements()[0]; + realStatement = oldExpr.replaceWithExpression(methodCall, true); } - return false; - } - - public enum MethodAccessQualifier { - PUBLIC, PRIVATE, PROTECTED + GrReferenceAdjuster.shortenReferences(realStatement); + return realStatement; } @NotNull - public static GrStatement[] createResultStatement(ExtractMethodInfoHelper helper, @NotNull String methodName) { - VariableInfo[] outputVars = helper.getOutputNames(); + private static GrStatement[] createResultStatement(ExtractInfoHelper helper) { + VariableInfo[] outputVars = helper.getOutputVariableInfos(); PsiType type = helper.getOutputType(); GrStatement[] statements = helper.getStatements(); - GrMethodCallExpression callExpression = createMethodCallByHelper(methodName, helper); + GrMethodCallExpression callExpression = createMethodCall(helper); if ((outputVars.length == 0 || PsiType.VOID.equals(type)) && !helper.hasReturnValue()) return new GrStatement[]{callExpression}; GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(helper.getProject()); @@ -108,7 +96,7 @@ public class ExtractUtil { return new GrStatement[]{factory.createStatementFromText("return " + callExpression.getText())}; } - assert outputVars.length > 0; + LOG.assertTrue(outputVars.length > 0); final List mustAdd = mustAddVariableDeclaration(statements, outputVars); if (mustAdd.size() == 0) { @@ -158,7 +146,7 @@ public class ExtractUtil { return diffTypes.size() > 1; } - private static GrStatement createAssignment(VariableInfo[] infos, GrMethodCallExpression callExpression, ExtractMethodInfoHelper helper) { + private static GrStatement createAssignment(VariableInfo[] infos, GrMethodCallExpression callExpression, ExtractInfoHelper helper) { StringBuilder text = new StringBuilder(); if (infos.length > 1) text.append('('); for (VariableInfo info : infos) { @@ -174,46 +162,7 @@ public class ExtractUtil { return GroovyPsiElementFactory.getInstance(helper.getProject()).createExpressionFromText(text.toString()); } - public static boolean validateMethod(GrMethod method, ExtractMethodInfoHelper helper) { - ArrayList conflicts = new ArrayList(); - GrMemberOwner owner = helper.getOwner(); - PsiMethod[] methods = ArrayUtil.mergeArrays(owner.getAllMethods(), new PsiMethod[]{method}, PsiMethod.ARRAY_FACTORY); - final Map> map = DuplicatesUtil.factorDuplicates(methods, new TObjectHashingStrategy() { - public int computeHashCode(PsiMethod method) { - return method.getSignature(PsiSubstitutor.EMPTY).hashCode(); - } - - public boolean equals(PsiMethod method1, PsiMethod method2) { - return method1.getSignature(PsiSubstitutor.EMPTY).equals(method2.getSignature(PsiSubstitutor.EMPTY)); - } - }); - - List list = map.get(method); - if (list == null) return true; - for (PsiMethod psiMethod : list) { - if (psiMethod != method) { - PsiClass containingClass = psiMethod.getContainingClass(); - if (containingClass == null) return true; - String message = containingClass instanceof GroovyScriptClass ? - GroovyRefactoringBundle.message("method.is.already.defined.in.script", GroovyRefactoringUtil.getMethodSignature(method), - CommonRefactoringUtil.htmlEmphasize(containingClass.getQualifiedName())) : - GroovyRefactoringBundle.message("method.is.already.defined.in.class", GroovyRefactoringUtil.getMethodSignature(method), - CommonRefactoringUtil.htmlEmphasize(containingClass.getQualifiedName())); - conflicts.add(message); - } - } - - return conflicts.size() <= 0 || reportConflicts(conflicts, helper.getProject()); - } - - static boolean reportConflicts(final ArrayList conflicts, final Project project) { - ConflictsDialog conflictsDialog = new ConflictsDialog(project, conflicts); - conflictsDialog.show(); - return conflictsDialog.isOK(); - } - - - public static void removeOldStatements(GrStatementOwner owner, ExtractMethodInfoHelper helper) throws IncorrectOperationException { + private static void removeOldStatements(GrStatementOwner owner, ExtractInfoHelper helper) throws IncorrectOperationException { owner.removeElements(helper.getInnerElements()); } @@ -247,49 +196,35 @@ public class ExtractUtil { return result; } - private static boolean containVariableDeclaration(@NotNull GrStatement[] statements, @NotNull String varName) { - GroovyPsiElement element = ResolveUtil.resolveProperty(statements[0], varName); - if (element == null) return false; - for (GrStatement statement : statements) { - if (statement.getTextRange().contains(element.getTextRange())) { - return true; - } - } - return false; - } + private static Collection collectUsedLocalVarsOrParamsDeclaredOutside(GrStatement[] statements) { + final Collection result = new HashSet(); - public static void renameParameterOccurrences(GrMethod method, ExtractMethodInfoHelper helper) throws IncorrectOperationException { - GrOpenBlock block = method.getBlock(); - if (block == null) return; - GrStatement[] statements = block.getStatements(); - - final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(helper.getProject()); - for (ParameterInfo info : helper.getParameterInfos()) { - final String oldName = info.getOldName(); - final String newName = info.getName(); - final ArrayList result = new ArrayList(); - if (!oldName.equals(newName)) { - for (final GrStatement statement : statements) { - statement.accept(new PsiRecursiveElementVisitor() { - public void visitElement(final PsiElement element) { - super.visitElement(element); - if (element instanceof GrReferenceExpression) { - GrReferenceExpression expr = (GrReferenceExpression) element; - if (!expr.isQualified() && oldName.equals(expr.getName())) { - result.add(expr); - } - } - } - }); - for (GrExpression expr : result) { - expr.replaceWithExpression(factory.createExpressionFromText(newName), false); + final int start = statements[0].getTextRange().getStartOffset(); + final int end = statements[statements.length - 1].getTextRange().getEndOffset(); + + final GroovyRecursiveElementVisitor visitor = new GroovyRecursiveElementVisitor() { + @Override + public void visitReferenceExpression(GrReferenceExpression ref) { + final PsiElement resolved = ref.resolve(); + if ((resolved instanceof GrParameter || + resolved instanceof GrVariable && GroovyRefactoringUtil.isLocalVariable((GrVariable)resolved)) && resolved.isPhysical()) { + final int offset = resolved.getTextRange().getStartOffset(); + //var is declared outside of selected code + if (offset < start || end <= offset) { + result.add((GrVariable)resolved); } } } + }; + + for (GrStatement statement : statements) { + statement.accept(visitor); } + + return result; } - public static GrMethod createMethodByHelper(@NotNull String name, ExtractMethodInfoHelper helper) { + public static GrMethod createMethod(ExtractMethodInfoHelper helper) { StringBuilder buffer = new StringBuilder(); //Add signature @@ -300,7 +235,7 @@ public class ExtractUtil { String typeText = getTypeString(helper, false, modifier); buffer.append(modifier); buffer.append(typeText); - buffer.append(name); + buffer.append(helper.getName()); buffer.append("("); for (String param : getParameterString(helper, true)) { buffer.append(param); @@ -308,27 +243,47 @@ public class ExtractUtil { buffer.append(") { \n"); GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(helper.getProject()); - VariableInfo[] outputInfos = helper.getOutputNames(); + generateBody(helper, type == PsiType.VOID, buffer); + + buffer.append("\n}"); + + String methodText = buffer.toString(); + GrMethod method = factory.createMethodFromText(methodText); + LOG.assertTrue(method != null); + return method; + } + + public static void generateBody(ExtractInfoHelper helper, boolean isVoid, StringBuilder buffer) { + VariableInfo[] outputInfos = helper.getOutputVariableInfos(); ParameterInfo[] infos = helper.getParameterInfos(); - boolean[] outputIsParameter = new boolean[outputInfos.length]; - for (int i = 0; i < outputInfos.length; i++) { - for (ParameterInfo info : infos) { - if (outputInfos[i].getName().equals(info.getOldName())) { - outputIsParameter[i] = true; - } - } + + Set declaredVars = new HashSet(); + for (ParameterInfo info : infos) { + declaredVars.add(info.getName()); } - final Set mustAdd = new HashSet(mustAddVariableDeclaration(helper.getStatements(), outputInfos)); + for (VariableInfo info : mustAddVariableDeclaration(helper.getStatements(), outputInfos)) { + declaredVars.add(info.getName()); + } List genDecl = new ArrayList(); - for (int i = 0; i < outputInfos.length; i++) { - VariableInfo info = outputInfos[i]; - if (type != PsiType.VOID && !outputIsParameter[i] && - !mustAdd.contains(info) && - !containVariableDeclaration(helper.getStatements(), info.getName())) { - genDecl.add(info); + final Collection outside = collectUsedLocalVarsOrParamsDeclaredOutside(helper.getStatements()); + + for (final GrVariable variable : outside) { + if (!declaredVars.contains(variable.getName())) { + genDecl.add(new VariableInfo() { + @NotNull + @Override + public String getName() { + return variable.getName(); + } + + @Override + public PsiType getType() { + return variable.getDeclaredType(); + } + }); } } final List statements = generateVarDeclarations(genDecl, helper.getProject(), null); @@ -341,7 +296,7 @@ public class ExtractUtil { buffer.append(element.getText()); } //append return statement - if (type != PsiType.VOID && outputInfos.length > 0) { + if (!isVoid && outputInfos.length > 0) { buffer.append("\n return "); if (outputInfos.length > 1) buffer.append('['); for (VariableInfo info : outputInfos) { @@ -353,15 +308,29 @@ public class ExtractUtil { } else { GrExpression expr = (GrExpression)PsiUtil.skipParentheses(helper.getStatements()[0], false); - buffer.append(PsiType.VOID.equals(type) ? "" : "return ").append(expr != null ? expr.getText() : ""); + boolean addReturn = !isVoid && expr != null && expr.getType() != null && expr.getType() != PsiType.VOID; + if (addReturn) { + buffer.append("return "); + if (expr instanceof GrApplicationStatement) { + final GrApplicationStatement appStatement = (GrApplicationStatement)expr; + + buffer.append(appStatement.getInvokedExpression().getText()); + buffer.append('('); + + final GrCommandArgumentList argList = appStatement.getArgumentList(); + if (argList != null) { + buffer.append(argList.getText()); + } + buffer.append(')'); + } + else { + buffer.append(expr.getText()); + } + } + else { + buffer.append(expr != null ? expr.getText() : ""); + } } - - buffer.append("\n}"); - - String methodText = buffer.toString(); - GrMethod method = factory.createMethodFromText(methodText); - assert method != null; - return method; } public static String[] getParameterString(ExtractInfoHelper helper, boolean useCanonicalText) { @@ -412,56 +381,14 @@ public class ExtractUtil { return typeText; } - public static GrStatement[] getStatementsByElements(PsiElement[] elements) { - ArrayList statementList = new ArrayList(); - for (PsiElement element : elements) { - if (element instanceof GrStatement) { - statementList.add(((GrStatement) element)); - } - } - return statementList.toArray(new GrStatement[statementList.size()]); - } - - public static PsiElement[] getElementsInOffset(PsiFile file, int startOffset, int endOffset) { - PsiElement[] elements; - GrExpression expr = GroovyRefactoringUtil.findElementInRange(file, startOffset, endOffset, GrExpression.class); - - if (expr != null) { - PsiElement parent = expr.getParent(); - if (expr.getParent() instanceof GrMethodCallExpression || parent instanceof GrIndexProperty) { - expr = ((GrExpression) expr.getParent()); - } - elements = new PsiElement[]{expr}; - } else { - elements = GroovyRefactoringUtil.findStatementsInRange(file, startOffset, endOffset, true); - } - return elements; - } - - @Nullable - public static GrMemberOwner getMemberOwner(GrStatement statement) { - PsiElement parent = statement.getParent(); - while (parent != null && !(parent instanceof GrMemberOwner)) { - if (parent instanceof GroovyFileBase) return (GrMemberOwner) ((GroovyFileBase) parent).getScriptClass(); - parent = parent.getParent(); - } - return parent != null ? ((GrMemberOwner) parent) : null; - } - - @Nullable - public static GrStatementOwner getDeclarationOwner(GrStatement statement) { - PsiElement parent = statement.getParent(); - return parent instanceof GrStatementOwner ? ((GrStatementOwner) parent) : null; - } - public static boolean isSingleExpression(GrStatement[] statements) { return statements.length == 1 && statements[0] instanceof GrExpression && !(statements[0].getParent() instanceof GrVariableDeclarationOwner && statements[0] instanceof GrAssignmentExpression); } - public static GrMethodCallExpression createMethodCallByHelper(@NotNull String name, ExtractMethodInfoHelper helper) { + private static GrMethodCallExpression createMethodCall(ExtractInfoHelper helper) { StringBuilder buffer = new StringBuilder(); - buffer.append(name).append("("); + buffer.append(helper.getName()).append("("); int number = 0; for (ParameterInfo info : helper.getParameterInfos()) { if (info.passAsParameter()) number++; @@ -482,7 +409,7 @@ public class ExtractUtil { String callText = buffer.toString(); GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(helper.getProject()); GrExpression expr = factory.createExpressionFromText(callText); - assert expr instanceof GrMethodCallExpression; + LOG.assertTrue(expr instanceof GrMethodCallExpression, callText); return ((GrMethodCallExpression) expr); } @@ -505,35 +432,10 @@ public class ExtractUtil { return statement.getTextOffset(); } - public static boolean canBeStatic(GrStatement statement) { - PsiElement parent = statement.getParent(); - while (parent != null && !(parent instanceof PsiFile)) { - if (parent instanceof GrMethod) { - return ((GrMethod) parent).hasModifierProperty(PsiModifier.STATIC); - } - parent = parent.getParent(); - } - return false; - } - public static String getModifierString(ExtractMethodInfoHelper helper) { String visibility = helper.getVisibility(); - assert visibility != null && visibility.length() > 0; + LOG.assertTrue(visibility != null && visibility.length() > 0); visibility = visibility.equals(PsiModifier.PUBLIC) ? "" : visibility + " "; return visibility + (helper.isStatic() ? "static " : ""); } - - public static boolean isReturnStatement(GrStatement statement, Collection returnStatements) { - if (statement instanceof GrReturnStatement) return true; - if (statement instanceof GrIfStatement) { - boolean checked = GroovyInlineMethodUtil.checkTailIfStatement(((GrIfStatement) statement), returnStatements); - return checked & returnStatements.size() == 0; - - } - if (statement instanceof GrExpression) { - return returnStatements.contains(statement); - } - return false; - } - } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/InitialInfo.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/InitialInfo.java index f25d36007bb9..569c4d44117f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/InitialInfo.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/InitialInfo.java @@ -42,7 +42,6 @@ public class InitialInfo implements ExtractInfoHelper { private final ParameterInfo[] myParameterInfos; private final VariableInfo[] myOutputNames; private final PsiType myOutputType; - private final GrMemberOwner myTargetClass; private final PsiElement[] myInnerElements; private final Project myProject; private final GrStatement[] myStatements; @@ -57,7 +56,6 @@ public class InitialInfo implements ExtractInfoHelper { ArrayList returnStatements) { myInnerElements = innerElements; myStatements = statements; - myTargetClass = targetClass; myOutputNames = outputInfos; myHasReturnValue = ContainerUtil.find(returnStatements, new Condition() { @@ -94,7 +92,7 @@ public class InitialInfo implements ExtractInfoHelper { outputType = outputInfos[0].getType(); } else { - outputType = JavaPsiFacade.getElementFactory(myProject).createTypeFromText(CommonClassNames.JAVA_UTIL_LIST, myTargetClass); + outputType = JavaPsiFacade.getElementFactory(myProject).createTypeFromText(CommonClassNames.JAVA_UTIL_LIST, myStatements[0]); } } else if (ExtractUtil.isSingleExpression(statements)) { @@ -137,7 +135,7 @@ public class InitialInfo implements ExtractInfoHelper { @Override @NotNull - public VariableInfo[] getOutputNames() { + public VariableInfo[] getOutputVariableInfos() { return myOutputNames; } @@ -170,13 +168,12 @@ public class InitialInfo implements ExtractInfoHelper { return myStatements; } - @Override - @NotNull - public GrMemberOwner getOwner() { - return myTargetClass; - } - public boolean hasReturnValue() { return myHasReturnValue; } + + @Override + public String getName() { + throw new UnsupportedOperationException(); + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureAction.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureAction.java new file mode 100644 index 000000000000..b77ed8198775 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureAction.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2012 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.plugins.groovy.refactoring.extract.closure; + +import com.intellij.lang.refactoring.RefactoringSupportProvider; +import com.intellij.refactoring.RefactoringActionHandler; +import com.intellij.refactoring.actions.BasePlatformRefactoringAction; +import org.jetbrains.annotations.NotNull; + +/** + * @author Max Medvedev + */ +public class ExtractClosureAction extends BasePlatformRefactoringAction { + @Override + protected RefactoringActionHandler getRefactoringHandler(@NotNull RefactoringSupportProvider provider) { + return new ExtractClosureHandler(); + } + + @Override + protected boolean isAvailableInEditorOnly() { + return true; + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureDialog.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureDialog.java new file mode 100644 index 000000000000..0c462fc02ef3 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureDialog.java @@ -0,0 +1,263 @@ +/* + * Copyright 2000-2012 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.plugins.groovy.refactoring.extract.closure; + +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.Splitter; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.psi.codeStyle.CodeStyleSettingsManager; +import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.refactoring.JavaRefactoringSettings; +import com.intellij.ui.EditorTextField; +import com.intellij.ui.IdeBorderFactory; +import com.intellij.ui.components.JBCheckBox; +import com.intellij.ui.components.JBLabel; +import com.intellij.util.ui.UIUtil; +import gnu.trove.TIntArrayList; +import gnu.trove.TObjectIntHashMap; +import gnu.trove.TObjectIntProcedure; +import org.jetbrains.plugins.groovy.GroovyFileType; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrParametersOwner; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; +import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle; +import org.jetbrains.plugins.groovy.refactoring.extract.ExtractUtil; +import org.jetbrains.plugins.groovy.refactoring.extract.InitialInfo; +import org.jetbrains.plugins.groovy.refactoring.extract.ParameterInfo; +import org.jetbrains.plugins.groovy.refactoring.extract.ParameterTablePanel; +import org.jetbrains.plugins.groovy.refactoring.ui.GrMethodSignatureComponent; + +import javax.swing.*; +import java.awt.*; + +/** + * @author Max Medvedev + */ +public class ExtractClosureDialog extends DialogWrapper { + + private final ParameterTablePanel myTable; + private final GrMethodSignatureComponent mySignature; + private final EditorTextField myNameField; + + private final ExtractClosureHelper myHelper; + private final JBCheckBox myFinalCB; + private final JBCheckBox myGenerateDelegateCB; + + TObjectIntHashMap toRemoveCBs; + private final InitialInfo myInitialInfo; + + public ExtractClosureDialog(InitialInfo initialInfo, GrParametersOwner owner, PsiElement toSearchFor) { + super(initialInfo.getProject()); + myInitialInfo = initialInfo; + + myHelper = new ExtractClosureHelper(initialInfo, owner, toSearchFor, "", false); + + setTitle(ExtractClosureHandler.EXTRACT_CLOSURE); + + myTable = new ParameterTablePanel() { + @Override + protected void updateSignature() { + ExtractClosureDialog.this.updateSignature(); + } + + @Override + protected void doEnterAction() { + clickDefaultButton(); + } + + @Override + protected void doCancelAction() { + ExtractClosureDialog.this.doCancelAction(); + } + }; + + mySignature = new GrMethodSignatureComponent("", initialInfo.getProject()); + myNameField = new EditorTextField("", initialInfo.getProject(), GroovyFileType.GROOVY_FILE_TYPE); + myFinalCB = new JBCheckBox(UIUtil.replaceMnemonicAmpersand("Declare &final")); + myFinalCB.setFocusable(false); + myGenerateDelegateCB = new JBCheckBox(UIUtil.replaceMnemonicAmpersand("De&legate via overloading method")); + myGenerateDelegateCB.setFocusable(false); + + TObjectIntHashMap parametersToRemove = findParametersToRemove(myHelper); + toRemoveCBs = new TObjectIntHashMap(parametersToRemove.size()); + for (Object p : parametersToRemove.keys()) { + JBCheckBox cb = new JBCheckBox(GroovyRefactoringBundle.message("remove.parameter.0.no.longer.used", ((GrParameter)p).getName())); + cb.setFocusable(false); + cb.setSelected(false); + toRemoveCBs.put(cb, parametersToRemove.get((GrParameter)p)); + } + + init(); + } + + @Override + protected ValidationInfo doValidate() { + final String text = myNameField.getText().trim(); + if (!StringUtil.isJavaIdentifier(text)) { + return new ValidationInfo(GroovyRefactoringBundle.message("name.is.wrong", text), myNameField); + } + + final Ref info = new Ref(); + toRemoveCBs.forEachEntry(new TObjectIntProcedure() { + @Override + public boolean execute(JBCheckBox checkbox, int index) { + if (!checkbox.isSelected()) return true; + + final ParameterInfo param = myHelper.getParameterInfos()[index]; + if (!param.passAsParameter()) return true; + + final ParameterInfo initialParam = myInitialInfo.getParameterInfos()[index]; + final String message = GroovyRefactoringBundle.message("you.cannot.pass.as.parameter.0.because.you.remove.1.from.base.method", + param.getName(), initialParam.getName()); + info.set(new ValidationInfo(message)); + return false; + } + }); + return info.get(); + } + + private void updateSignature() { + StringBuilder b = new StringBuilder(); + b.append("{ "); + String[] params = ExtractUtil.getParameterString(myHelper, false); + for (int i = 0; i < params.length; i++) { + if (i > 0) { + b.append(" "); + } + b.append(params[i]); + b.append('\n'); + } + b.append(" ->\n}"); + mySignature.setSignature(b.toString()); + } + + @Override + public JComponent getPreferredFocusedComponent() { + return myNameField; + } + + @Override + protected void init() { + super.init(); + + myTable.init(myHelper); + + final JavaRefactoringSettings refactoringSettings = JavaRefactoringSettings.getInstance(); + final Boolean settingsFinals = refactoringSettings.INTRODUCE_PARAMETER_CREATE_FINALS; + myFinalCB.setSelected(settingsFinals == null ? + CodeStyleSettingsManager.getSettings(myHelper.getProject()).GENERATE_FINAL_PARAMETERS : + settingsFinals.booleanValue()); + myGenerateDelegateCB.setSelected(false); + updateSignature(); + } + + @Override + protected JComponent createNorthPanel() { + final JPanel panel = new JPanel(new BorderLayout()); + final JBLabel nameLabel = new JBLabel(); + panel.add(nameLabel, BorderLayout.NORTH); + panel.add(myNameField, BorderLayout.CENTER); + nameLabel.setText(UIUtil.replaceMnemonicAmpersand("Parameter &name:")); + nameLabel.setLabelFor(myNameField); + + final JPanel checkBoxPanel = new JPanel(); + checkBoxPanel.setLayout(new BoxLayout(checkBoxPanel, BoxLayout.Y_AXIS)); + + checkBoxPanel.add(myFinalCB); + checkBoxPanel.add(myGenerateDelegateCB); + for (Object cb : toRemoveCBs.keys()) { + checkBoxPanel.add((Component)cb); + } + panel.add(checkBoxPanel, BorderLayout.SOUTH); + return panel; + } + + @Override + protected JComponent createCenterPanel() { + final JPanel signaturePanel = new JPanel(new BorderLayout()); + signaturePanel.setBorder( + IdeBorderFactory.createTitledBorder(GroovyRefactoringBundle.message("signature.preview.border.title"), false, false, true)); + signaturePanel.add(mySignature, BorderLayout.CENTER); + + Splitter splitter = new Splitter(true); + + splitter.setFirstComponent(myTable); + splitter.setSecondComponent(signaturePanel); + + mySignature.setPreferredSize(new Dimension(500, 100)); + mySignature.setSize(new Dimension(500, 100)); + + splitter.setShowDividerIcon(false); + return splitter; + } + + @Override + protected void doOKAction() { + final JavaRefactoringSettings settings = JavaRefactoringSettings.getInstance(); + settings.INTRODUCE_PARAMETER_CREATE_FINALS = Boolean.valueOf(myFinalCB.isSelected()); + + myHelper.setName(myNameField.getText()); + myHelper.setDeclareFinal(myFinalCB.isSelected()); + myHelper.setGenerateDelegate(myGenerateDelegateCB.isSelected()); + + TIntArrayList list = new TIntArrayList(); + for (Object cb : toRemoveCBs.keys()) { + final JBCheckBox checkbox = (JBCheckBox)cb; + if (checkbox.isSelected()) { + list.add(toRemoveCBs.get(checkbox)); + } + } + myHelper.setToRemove(list); + super.doOKAction(); + } + + public ExtractClosureHelper getHelper() { + return myHelper; + } + + private static TObjectIntHashMap findParametersToRemove(ExtractClosureHelper helper) { + final TObjectIntHashMap result = new TObjectIntHashMap(); + + final GrStatement[] statements = helper.getStatements(); + final int start = statements[0].getTextRange().getStartOffset(); + final int end = statements[statements.length - 1].getTextRange().getEndOffset(); + + GrParameter[] parameters = helper.getOwner().getParameters(); + for (int i = 0; i < parameters.length; i++) { + GrParameter parameter = parameters[i]; + if (shouldRemove(parameter, start, end)) { + result.put(parameter, i); + } + } + return result; + } + + private static boolean shouldRemove(GrParameter parameter, int start, int end) { + for (PsiReference reference : ReferencesSearch.search(parameter)) { + final PsiElement element = reference.getElement(); + if (element == null) continue; + + final int offset = element.getTextRange().getStartOffset(); + if (offset < start || end <= offset) { + return false; + } + } + return true; + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureFromClosureProcessor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureFromClosureProcessor.java new file mode 100644 index 000000000000..ca8bf33e1253 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureFromClosureProcessor.java @@ -0,0 +1,79 @@ +/* + * Copyright 2000-2012 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.plugins.groovy.refactoring.extract.closure; + +import com.intellij.openapi.util.Ref; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.refactoring.introduceParameter.ExternalUsageInfo; +import com.intellij.usageView.UsageInfo; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.GroovyFileType; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall; +import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Max Medvedev + */ +public class ExtractClosureFromClosureProcessor extends ExtractClosureProcessorBase { + public ExtractClosureFromClosureProcessor(@NotNull ExtractClosureHelper helper) { + super(helper); + } + + @Override + protected boolean preprocessUsages(Ref refUsages) { + //todo + return true; + } + + + @Override + protected void performRefactoring(UsageInfo[] usages) { + //To change body of implemented methods use File | Settings | File Templates. todo + } + + @NotNull + @Override + protected UsageInfo[] findUsages() { + final List result = new ArrayList(); + + final GrVariable var = (GrVariable)myHelper.getToSearchFor(); + + for (PsiReference ref : ReferencesSearch.search(var, GlobalSearchScope.allScope(myHelper.getProject()), true)) { + final PsiElement element = ref.getElement(); + if (element.getLanguage() != GroovyFileType.GROOVY_LANGUAGE) { + result.add(new OtherLanguageUsageInfo(ref)); + continue; + } + + final GrCall call = GroovyRefactoringUtil.getCallExpressionByMethodReference(element); + if (call == null) continue; + + result.add(new ExternalUsageInfo(element)); + } + + return result.toArray(new UsageInfo[result.size()]); + } + + +} + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureFromMethodProcessor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureFromMethodProcessor.java new file mode 100644 index 000000000000..db0266647df2 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureFromMethodProcessor.java @@ -0,0 +1,270 @@ +/* + * Copyright 2000-2012 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.plugins.groovy.refactoring.extract.closure; + +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.searches.MethodReferencesSearch; +import com.intellij.psi.search.searches.OverridingMethodsSearch; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.refactoring.IntroduceParameterRefactoring; +import com.intellij.refactoring.RefactoringBundle; +import com.intellij.refactoring.introduceParameter.ChangedMethodCallInfo; +import com.intellij.refactoring.introduceParameter.ExternalUsageInfo; +import com.intellij.refactoring.introduceParameter.InternalUsageInfo; +import com.intellij.refactoring.introduceParameter.IntroduceParameterData; +import com.intellij.refactoring.util.CommonRefactoringUtil; +import com.intellij.refactoring.util.usageInfo.DefaultConstructorImplicitUsageInfo; +import com.intellij.refactoring.util.usageInfo.NoConstructorClassUsageInfo; +import com.intellij.usageView.UsageInfo; +import com.intellij.usageView.UsageViewUtil; +import com.intellij.util.containers.MultiMap; +import gnu.trove.TIntArrayList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.GroovyFileType; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; +import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames; +import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle; +import org.jetbrains.plugins.groovy.refactoring.extract.ExtractUtil; +import org.jetbrains.plugins.groovy.refactoring.introduce.parameter.AnySupers; +import org.jetbrains.plugins.groovy.refactoring.introduce.parameter.FieldConflictsResolver; +import org.jetbrains.plugins.groovy.refactoring.introduce.parameter.GrExpressionWrapper; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static com.intellij.refactoring.introduceParameter.IntroduceParameterUtil.changeMethodSignatureAndResolveFieldConflicts; +import static com.intellij.refactoring.introduceParameter.IntroduceParameterUtil.processUsages; +import static org.jetbrains.plugins.groovy.refactoring.introduce.parameter.GroovyIntroduceParameterUtil.*; + +/** + * @author Max Medvedev + */ +public class ExtractClosureFromMethodProcessor extends ExtractClosureProcessorBase { + + private final GrMethod myMethod; + private Editor myEditor; + + public ExtractClosureFromMethodProcessor(@NotNull ExtractClosureHelper helper, Editor editor) { + super(helper); + myEditor = editor; + myMethod = (GrMethod)myHelper.getOwner(); + } + + @Override + protected boolean preprocessUsages(Ref refUsages) { + UsageInfo[] usagesIn = refUsages.get(); + MultiMap conflicts = new MultiMap(); + final GrStatement[] statements = myHelper.getStatements(); + + for (GrStatement statement : statements) { + detectAccessibilityConflicts(statement, usagesIn, conflicts, false, myProject); + } + + for (UsageInfo info : usagesIn) { + if (info instanceof OtherLanguageUsageInfo) { + conflicts.putValue(info.getElement(), GroovyRefactoringBundle.message("cannot.process.usage.in.language.{0}", CommonRefactoringUtil.htmlEmphasize(info.getElement().getLanguage().getDisplayName()))); + } + } + + if (!myMethod.hasModifierProperty(PsiModifier.PRIVATE)) { + final AnySupers anySupers = new AnySupers(); + for (GrStatement statement : statements) { + statement.accept(anySupers); + } + if (anySupers.isResult()) { + for (UsageInfo usageInfo : usagesIn) { + if (!(usageInfo.getElement() instanceof PsiMethod) && !(usageInfo instanceof InternalUsageInfo)) { + if (!PsiTreeUtil.isAncestor(myMethod.getContainingClass(), usageInfo.getElement(), false)) { + conflicts.putValue(statements[0], RefactoringBundle + .message("parameter.initializer.contains.0.but.not.all.calls.to.method.are.in.its.class", + CommonRefactoringUtil.htmlEmphasize(PsiKeyword.SUPER))); + break; + } + } + } + } + } + + return showConflicts(conflicts, usagesIn); + } + + @NotNull + @Override + protected UsageInfo[] findUsages() { + List result = new ArrayList(); + + final PsiMethod toSearchFor = (PsiMethod)myHelper.getToSearchFor(); + + for (PsiReference ref1 : MethodReferencesSearch.search(toSearchFor, GlobalSearchScope.projectScope(myProject), true)) { + PsiElement ref = ref1.getElement(); + if (ref.getLanguage() != GroovyFileType.GROOVY_LANGUAGE) { + result.add(new OtherLanguageUsageInfo(ref1)); + continue; + } + + if (ref instanceof PsiMethod && ((PsiMethod)ref).isConstructor()) { + DefaultConstructorImplicitUsageInfo implicitUsageInfo = + new DefaultConstructorImplicitUsageInfo((PsiMethod)ref, ((PsiMethod)ref).getContainingClass(), toSearchFor); + result.add(implicitUsageInfo); + } + else if (ref instanceof PsiClass) { + result.add(new NoConstructorClassUsageInfo((PsiClass)ref)); + } + else if (!PsiTreeUtil.isAncestor(myMethod, ref, false)) { + result.add(new ExternalUsageInfo(ref)); + } + else { + result.add(new ChangedMethodCallInfo(ref)); + } + } + + Collection overridingMethods = OverridingMethodsSearch.search(toSearchFor, true).findAll(); + + for (PsiMethod overridingMethod : overridingMethods) { + result.add(new UsageInfo(overridingMethod)); + } + + final UsageInfo[] usageInfos = result.toArray(new UsageInfo[result.size()]); + return UsageViewUtil.removeDuplicatedUsages(usageInfos); + } + + + @Override + protected void performRefactoring(UsageInfo[] usages) { + final IntroduceParameterData data = new IntroduceParameterDataAdapter(); + + processUsages(usages, data); + + final PsiMethod toSearchFor = (PsiMethod)myHelper.getToSearchFor(); + + final boolean methodsToProcessAreDifferent = myMethod != toSearchFor; + if (myHelper.generateDelegate()) { + generateDelegate(myMethod, data.getParameterInitializer(), myProject); + if (methodsToProcessAreDifferent) { + final GrMethod method = generateDelegate(toSearchFor, data.getParameterInitializer(), myProject); + final PsiClass containingClass = method.getContainingClass(); + if (containingClass != null && containingClass.isInterface()) { + final GrOpenBlock block = method.getBlock(); + if (block != null) { + block.delete(); + } + } + } + } + + // Changing signature of initial method + // (signature of myMethodToReplaceIn will be either changed now or have already been changed) + final FieldConflictsResolver fieldConflictsResolver = new FieldConflictsResolver(myHelper.getName(), myMethod.getBlock()); + changeMethodSignatureAndResolveFieldConflicts(new UsageInfo(myMethod), usages, data); + if (methodsToProcessAreDifferent) { + changeMethodSignatureAndResolveFieldConflicts(new UsageInfo(toSearchFor), usages, data); + } + + // Replacing expression occurrences + for (UsageInfo usage : usages) { + if (usage instanceof ChangedMethodCallInfo) { + PsiElement element = usage.getElement(); + + processChangedMethodCall(element, myHelper, myProject); + } + } + + final GrStatement newStatement = ExtractUtil.replaceStatement(myMethod.getBlock(), myHelper); + if (myEditor != null) { + PsiDocumentManager.getInstance(myProject).commitDocument(myEditor.getDocument()); + myEditor.getCaretModel().moveToOffset(ExtractUtil.getCaretOffset(newStatement)); + } + + fieldConflictsResolver.fix(); + } + + private class IntroduceParameterDataAdapter implements IntroduceParameterData { + + private final GrClosableBlock myClosure; + private final GrExpressionWrapper myWrapper; + private final PsiType myType; + + private IntroduceParameterDataAdapter() { + myClosure = generateClosure(); + myWrapper = new GrExpressionWrapper(myClosure); + myType = JavaPsiFacade.getElementFactory(myProject).createTypeFromText(GroovyCommonClassNames.GROOVY_LANG_CLOSURE, myMethod); + } + + @NotNull + @Override + public Project getProject() { + return myProject; + } + + @Override + public PsiMethod getMethodToReplaceIn() { + return myMethod; + } + + @NotNull + @Override + public PsiMethod getMethodToSearchFor() { + return (PsiMethod)myHelper.getToSearchFor(); + } + + @Override + public ExpressionWrapper getParameterInitializer() { + return myWrapper; + } + + @NotNull + @Override + public String getParameterName() { + return myHelper.getName(); + } + + @Override + public int getReplaceFieldsWithGetters() { + return IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_NONE; //todo add option to dialog + } + + @Override + public boolean isDeclareFinal() { + return myHelper.declareFinal(); + } + + @Override + public boolean isGenerateDelegate() { + return false; //todo + } + + @NotNull + @Override + public PsiType getForcedType() { + return myType; + } + + @NotNull + @Override + public TIntArrayList getParametersToRemove() { + return myHelper.parametersToRemove(); + } + + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureHandler.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureHandler.java new file mode 100644 index 000000000000..f0931a3c9b01 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureHandler.java @@ -0,0 +1,173 @@ +/* + * Copyright 2000-2012 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.plugins.groovy.refactoring.extract.closure; + +import com.intellij.ide.util.SuperMethodWarningUtil; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.SelectionModel; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.JBPopup; +import com.intellij.openapi.util.Pass; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.refactoring.IntroduceTargetChooser; +import com.intellij.refactoring.RefactoringActionHandler; +import com.intellij.refactoring.RefactoringBundle; +import com.intellij.refactoring.util.CommonRefactoringUtil; +import com.intellij.util.Function; +import com.intellij.util.PairFunction; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrParametersOwner; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrMemberOwner; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; +import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner; +import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle; +import org.jetbrains.plugins.groovy.refactoring.HelpID; +import org.jetbrains.plugins.groovy.refactoring.extract.ExtractException; +import org.jetbrains.plugins.groovy.refactoring.extract.ExtractHandlerBase; +import org.jetbrains.plugins.groovy.refactoring.extract.InitialInfo; +import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceHandlerBase; +import org.jetbrains.plugins.groovy.refactoring.ui.MethodOrClosureScopeChooser; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Max Medvedev + */ +public class ExtractClosureHandler extends ExtractHandlerBase implements RefactoringActionHandler, MethodOrClosureScopeChooser.JBPopupOwner { + public static final String EXTRACT_CLOSURE = "Extract Closure"; + private JBPopup myPopup; + + @Override + public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file, DataContext dataContext) { + final SelectionModel selectionModel = editor.getSelectionModel(); + if (!selectionModel.hasSelection()) { + final int offset = editor.getCaretModel().getOffset(); + + final List expressions = GrIntroduceHandlerBase.collectExpressions(file, editor, offset); + if (expressions.size() == 1) { + final TextRange textRange = expressions.get(0).getTextRange(); + selectionModel.setSelection(textRange.getStartOffset(), textRange.getEndOffset()); + } + else { + final Pass callback = new Pass() { + public void pass(final GrExpression selectedValue) { + invoke(project, editor, file, selectedValue.getTextRange().getStartOffset(), selectedValue.getTextRange().getEndOffset()); + } + }; + final Function renderer = new Function() { + @Override + public String fun(GrExpression grExpression) { + return grExpression.getText(); + } + }; + IntroduceTargetChooser.showChooser(editor, expressions, callback, renderer); + return; + } + } + + invoke(project, editor, file, selectionModel.getSelectionStart(), selectionModel.getSelectionEnd()); + } + + void invoke(Project project, Editor editor, PsiFile file, int start, int end) { + try { + invokeOnEditor(project, editor, file, start, end); + } + catch (ExtractException e) { + CommonRefactoringUtil.showErrorHint(project, editor, e.getMessage(), EXTRACT_CLOSURE, HelpID.GROOVY_EXTRACT_CLOSURE); + } + } + + @Nullable + protected ExtractClosureHelper getSettings(@NotNull InitialInfo initialInfo, GrParametersOwner owner, PsiElement toSearchFor) { + final ExtractClosureDialog dialog = new ExtractClosureDialog(initialInfo, owner, toSearchFor); + dialog.show(); + if (!dialog.isOK()) return null; + + return dialog.getHelper(); + } + + + private void findScope(@NotNull PsiElement place, + @NotNull final Editor editor, + @NotNull PairFunction callback) { + final List scopes = new ArrayList(); + while (true) { + final GrParametersOwner parent = PsiTreeUtil.getParentOfType(place, GrMethod.class, GrClosableBlock.class); + if (parent == null) break; + scopes.add(parent); + place = parent; + } + + if (scopes.size() == 0) { + throw new ExtractException(GroovyRefactoringBundle.message("there.is.no.method.or.closure")); + } + else if (scopes.size() == 1) { + final GrParametersOwner owner = scopes.get(0); + if (owner instanceof GrMethod) { + PsiMethod newMethod = SuperMethodWarningUtil.checkSuperMethod((PsiMethod)owner, RefactoringBundle.message("to.refactor")); + if (newMethod == null) return; + callback.fun(owner, newMethod); + } + else { + callback.fun(owner, MethodOrClosureScopeChooser.findVariableToUse(owner)); + } + } + else { + myPopup = MethodOrClosureScopeChooser.create(scopes, editor, this, callback); + myPopup.showInBestPositionFor(editor); + } + } + + + @Override + public void performRefactoring(@NotNull final InitialInfo info, + @NotNull GrMemberOwner owner, + GrStatementOwner declarationOwner, + final Editor editor, + PsiElement startElement) { + findScope(startElement, editor, new PairFunction() { + @Override + public Object fun(GrParametersOwner owner, PsiElement toSearchFor) { + final ExtractClosureHelper helper = getSettings(info, owner, toSearchFor); + if (helper == null) return null; + + if (helper.getOwner() instanceof GrMethod) { + new ExtractClosureFromMethodProcessor(helper, editor).run(); + } + return null; + } + }); + } + + @Override + public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) { + //do nothing + } + + @Override + public JBPopup get() { + return myPopup; + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureHelper.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureHelper.java new file mode 100644 index 000000000000..188b10ca1388 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureHelper.java @@ -0,0 +1,111 @@ +/* + * Copyright 2000-2012 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.plugins.groovy.refactoring.extract.closure; + + +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiType; +import gnu.trove.TIntArrayList; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrParametersOwner; +import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames; +import org.jetbrains.plugins.groovy.refactoring.extract.ExtractInfoHelper; +import org.jetbrains.plugins.groovy.refactoring.extract.ExtractInfoHelperBase; +import org.jetbrains.plugins.groovy.refactoring.extract.InitialInfo; +import org.jetbrains.plugins.groovy.refactoring.introduce.parameter.GrIntroduceParameterSettings; + +/** + * @author Max Medvedev + */ +public class ExtractClosureHelper extends ExtractInfoHelperBase implements ExtractInfoHelper, GrIntroduceParameterSettings { + + private final GrParametersOwner myOwner; + private final PsiElement myToSearchFor; + + private String myName; + private boolean myFinal; + private TIntArrayList toRemove; + private boolean myGenerateDelegate; + + public ExtractClosureHelper(InitialInfo info, GrParametersOwner owner, PsiElement toSearchFor, String name, boolean declareFinal) { + super(info); + myOwner = owner; + myToSearchFor = toSearchFor; + myName = name; + myFinal = declareFinal; + } + + public GrParametersOwner getOwner() { + return myOwner; + } + + public PsiElement getToSearchFor() { + return myToSearchFor; + } + + public String getName() { + return myName; + } + + public boolean declareFinal() { + return myFinal; + } + + public void setName(String name) { + myName = name; + } + + public void setDeclareFinal(boolean aFinal) { + myFinal = aFinal; + } + + public void setToRemove(TIntArrayList toRemove) { + this.toRemove = toRemove; + } + + @Override + public TIntArrayList parametersToRemove() { + return toRemove; + } + + @Override + public int replaceFieldsWithGetters() { + return 0;//todo + } + + @Override + public boolean removeLocalVariable() { + return false; + } + + @Override + public boolean replaceAllOccurrences() { + return false; + } + + @Override + public PsiType getSelectedType() { + return JavaPsiFacade.getElementFactory(getProject()).createTypeFromText(GroovyCommonClassNames.GROOVY_LANG_CLOSURE, myOwner); + } + + public boolean generateDelegate() { + return myGenerateDelegate; + } + + public void setGenerateDelegate(boolean generateDelegate) { + myGenerateDelegate = generateDelegate; + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureProcessorBase.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureProcessorBase.java new file mode 100644 index 000000000000..f65e361c69bf --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureProcessorBase.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2012 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.plugins.groovy.refactoring.extract.closure; + +import com.intellij.psi.PsiElement; +import com.intellij.refactoring.BaseRefactoringProcessor; +import com.intellij.refactoring.ui.UsageViewDescriptorAdapter; +import com.intellij.usageView.UsageInfo; +import com.intellij.usageView.UsageViewDescriptor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; +import org.jetbrains.plugins.groovy.refactoring.extract.ExtractUtil; + +/** + * @author Max Medvedev + */ +public abstract class ExtractClosureProcessorBase extends BaseRefactoringProcessor { + protected final ExtractClosureHelper myHelper; + + public ExtractClosureProcessorBase(@NotNull ExtractClosureHelper helper) { + super(helper.getProject()); + myHelper = helper; + } + + @NotNull + @Override + protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usages) { + return new UsageViewDescriptorAdapter() { + @NotNull + @Override + public PsiElement[] getElements() { + return new PsiElement[]{myHelper.getToSearchFor()}; + } + + @Override + public String getProcessedElementsHeader() { + return ExtractClosureHandler.EXTRACT_CLOSURE; + } + }; + } + + @Override + protected String getCommandName() { + return ExtractClosureHandler.EXTRACT_CLOSURE; + } + + protected GrClosableBlock generateClosure() { + StringBuilder buffer = new StringBuilder(); + + buffer.append('{'); + for (String p : ExtractUtil.getParameterString(myHelper, true)) { + buffer.append(p); + } + buffer.append("->\n"); + + ExtractUtil.generateBody(myHelper, false, buffer); + buffer.append('}'); + + return GroovyPsiElementFactory.getInstance(myHelper.getProject()).createClosureFromText(buffer.toString(), myHelper.getOwner()); + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/OtherLanguageUsageInfo.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/OtherLanguageUsageInfo.java new file mode 100644 index 000000000000..97bd9e7c5d39 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/closure/OtherLanguageUsageInfo.java @@ -0,0 +1,29 @@ +/* + * Copyright 2000-2012 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.plugins.groovy.refactoring.extract.closure; + +import com.intellij.psi.PsiReference; +import com.intellij.usageView.UsageInfo; +import org.jetbrains.annotations.NotNull; + +/** + * @author Max Medvedev + */ +public class OtherLanguageUsageInfo extends UsageInfo { + public OtherLanguageUsageInfo(@NotNull PsiReference reference) { + super(reference); + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/method/ExtractMethodInfoHelper.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/method/ExtractMethodInfoHelper.java index ed87a8dd84a5..f961980a6e28 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/method/ExtractMethodInfoHelper.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/method/ExtractMethodInfoHelper.java @@ -16,9 +16,14 @@ package org.jetbrains.plugins.groovy.refactoring.extract.method; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; import com.intellij.psi.PsiModifier; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrMemberOwner; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; import org.jetbrains.plugins.groovy.refactoring.extract.ExtractInfoHelperBase; -import org.jetbrains.plugins.groovy.refactoring.extract.ExtractUtil; import org.jetbrains.plugins.groovy.refactoring.extract.InitialInfo; /** @@ -30,14 +35,16 @@ public class ExtractMethodInfoHelper extends ExtractInfoHelperBase { private boolean mySpecifyType = true; private String myVisibility; private String myName; + private final GrMemberOwner myOwner; - public ExtractMethodInfoHelper(InitialInfo initialInfo, String name) { + public ExtractMethodInfoHelper(InitialInfo initialInfo, String name, GrMemberOwner owner) { super(initialInfo); + myOwner = owner; myVisibility = PsiModifier.PRIVATE; myName = name; - myIsStatic = ExtractUtil.canBeStatic(initialInfo.getStatements()[0]); + myIsStatic = canBeStatic(initialInfo.getStatements()[0]); } public boolean isStatic() { @@ -67,4 +74,20 @@ public class ExtractMethodInfoHelper extends ExtractInfoHelperBase { public void setName(String name) { myName = name; } + + @NotNull + public GrMemberOwner getOwner() { + return myOwner; + } + + private static boolean canBeStatic(GrStatement statement) { + PsiElement parent = statement.getParent(); + while (parent != null && !(parent instanceof PsiFile)) { + if (parent instanceof GrMethod) { + return ((GrMethod) parent).hasModifierProperty(PsiModifier.STATIC); + } + parent = parent.getParent(); + } + return false; + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/method/GroovyExtractMethodDialog.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/method/GroovyExtractMethodDialog.java index d6f2b0425d9c..8d888a0a7f0f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/method/GroovyExtractMethodDialog.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/method/GroovyExtractMethodDialog.java @@ -22,19 +22,30 @@ import com.intellij.openapi.help.HelpManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Splitter; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiModifier; +import com.intellij.psi.PsiSubstitutor; import com.intellij.refactoring.HelpID; import com.intellij.refactoring.ui.ComboBoxVisibilityPanel; +import com.intellij.refactoring.ui.ConflictsDialog; import com.intellij.refactoring.ui.MethodSignatureComponent; +import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.ui.EditorTextField; import com.intellij.ui.IdeBorderFactory; +import com.intellij.util.ArrayUtil; +import gnu.trove.TObjectHashingStrategy; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.GroovyFileType; +import org.jetbrains.plugins.groovy.intentions.utils.DuplicatesUtil; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrMemberOwner; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; +import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass; import org.jetbrains.plugins.groovy.refactoring.GroovyNamesUtil; import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle; +import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil; import org.jetbrains.plugins.groovy.refactoring.extract.ExtractUtil; import org.jetbrains.plugins.groovy.refactoring.extract.InitialInfo; import org.jetbrains.plugins.groovy.refactoring.extract.ParameterTablePanel; @@ -51,7 +62,10 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; +import java.util.ArrayList; import java.util.EventListener; +import java.util.List; +import java.util.Map; /** * @author ilyas @@ -71,10 +85,10 @@ public class GroovyExtractMethodDialog extends DialogWrapper { private ParameterTablePanel myParameterTablePanel; private final Project myProject; - public GroovyExtractMethodDialog(InitialInfo info) { + public GroovyExtractMethodDialog(InitialInfo info, GrMemberOwner owner) { super(info.getProject(), true); myProject = info.getProject(); - myHelper = new ExtractMethodInfoHelper(info, ""); + myHelper = new ExtractMethodInfoHelper(info, "", owner); setUpNameField(); myParameterTablePanel.init(myHelper); @@ -98,8 +112,8 @@ public class GroovyExtractMethodDialog extends DialogWrapper { protected void doOKAction() { String name = getEnteredName(); if (name == null) return; - GrMethod method = ExtractUtil.createMethodByHelper(name, myHelper); - if (method != null && !ExtractUtil.validateMethod(method, myHelper)) { + GrMethod method = ExtractUtil.createMethod(myHelper); + if (method != null && !validateMethod(method, myHelper)) { return; } if (myCbSpecifyType.isEnabled()) { @@ -232,6 +246,44 @@ public class GroovyExtractMethodDialog extends DialogWrapper { }; } + private static boolean validateMethod(GrMethod method, ExtractMethodInfoHelper helper) { + ArrayList conflicts = new ArrayList(); + GrMemberOwner owner = helper.getOwner(); + PsiMethod[] methods = ArrayUtil.mergeArrays(owner.getAllMethods(), new PsiMethod[]{method}, PsiMethod.ARRAY_FACTORY); + final Map> map = DuplicatesUtil.factorDuplicates(methods, new TObjectHashingStrategy() { + public int computeHashCode(PsiMethod method) { + return method.getSignature(PsiSubstitutor.EMPTY).hashCode(); + } + + public boolean equals(PsiMethod method1, PsiMethod method2) { + return method1.getSignature(PsiSubstitutor.EMPTY).equals(method2.getSignature(PsiSubstitutor.EMPTY)); + } + }); + + List list = map.get(method); + if (list == null) return true; + for (PsiMethod psiMethod : list) { + if (psiMethod != method) { + PsiClass containingClass = psiMethod.getContainingClass(); + if (containingClass == null) return true; + String message = containingClass instanceof GroovyScriptClass ? + GroovyRefactoringBundle.message("method.is.already.defined.in.script", GroovyRefactoringUtil.getMethodSignature(method), + CommonRefactoringUtil.htmlEmphasize(containingClass.getQualifiedName())) : + GroovyRefactoringBundle.message("method.is.already.defined.in.class", GroovyRefactoringUtil.getMethodSignature(method), + CommonRefactoringUtil.htmlEmphasize(containingClass.getQualifiedName())); + conflicts.add(message); + } + } + + return conflicts.size() <= 0 || reportConflicts(conflicts, helper.getProject()); + } + + private static boolean reportConflicts(final ArrayList conflicts, final Project project) { + ConflictsDialog conflictsDialog = new ConflictsDialog(project, conflicts); + conflictsDialog.show(); + return conflictsDialog.isOK(); + } + class DataChangedListener implements EventListener { void dataChanged() { update(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/method/GroovyExtractMethodHandler.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/method/GroovyExtractMethodHandler.java index ec50b31297f3..4522d2df7666 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/method/GroovyExtractMethodHandler.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/extract/method/GroovyExtractMethodHandler.java @@ -17,9 +17,9 @@ package org.jetbrains.plugins.groovy.refactoring.extract.method; import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.application.AccessToken; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.editor.SelectionModel; @@ -29,27 +29,29 @@ import com.intellij.psi.tree.IElementType; import com.intellij.refactoring.RefactoringActionHandler; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.GrReferenceAdjuster; import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.lexer.TokenSets; +import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrMemberOwner; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner; -import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle; -import org.jetbrains.plugins.groovy.refactoring.extract.ExtractException; -import org.jetbrains.plugins.groovy.refactoring.extract.ExtractHandlerBase; -import org.jetbrains.plugins.groovy.refactoring.extract.ExtractUtil; -import org.jetbrains.plugins.groovy.refactoring.extract.InitialInfo; +import org.jetbrains.plugins.groovy.refactoring.extract.*; + +import java.util.ArrayList; /** * @author ilyas */ -public class GroovyExtractMethodHandler extends ExtractHandlerBase implements RefactoringActionHandler { - - private static final Logger LOG = Logger.getInstance(GroovyExtractMethodHandler.class); +public class GroovyExtractMethodHandler extends ExtractHandlerBase implements RefactoringActionHandler { protected static String REFACTORING_NAME = GroovyRefactoringBundle.message("extract.method.title"); private String myInvokeResult = "ok"; @@ -73,85 +75,60 @@ public class GroovyExtractMethodHandler extends ExtractHandlerBase 0; - realStatement = null; - for (GrStatement statement : newStatement) { - realStatement = declarationOwner.addStatementBefore(statement, statements[0]); - } - assert realStatement != null; - // remove old statements - ExtractUtil.removeOldStatements(declarationOwner, helper); - PsiImplUtil.removeNewLineAfter(realStatement); - } - else { - // Expression call replace - GrExpression methodCall = ExtractUtil.createMethodCallByHelper(methodName, helper); - GrExpression oldExpr = (GrExpression)helper.getStatements()[0]; - realStatement = oldExpr.replaceWithExpression(methodCall, true); - } - GrReferenceAdjuster.shortenReferences(realStatement); - - PsiElement prev = newMethod.getPrevSibling(); - IElementType elementType = prev.getNode().getElementType(); - if (!TokenSets.WHITE_SPACES_SET.contains(elementType) || !prev.getText().contains("\n")) { - newMethod.getParent().getNode().addLeaf(GroovyTokenTypes.mNLS, "\n", newMethod.getNode()); - } + createMethod(helper, owner, startElement); + GrStatement realStatement = ExtractUtil.replaceStatement(declarationOwner, helper); // move to offset if (editor != null) { PsiDocumentManager.getInstance(helper.getProject()).commitDocument(editor.getDocument()); + editor.getSelectionModel().removeSelection(); editor.getCaretModel().moveToOffset(ExtractUtil.getCaretOffset(realStatement)); } } - catch (IncorrectOperationException e) { - LOG.error(e); + finally { + lock.finish(); } } - }; - - Project project = helper.getProject(); - CommandProcessor.getInstance().executeCommand(project, new Runnable() { - public void run() { - ApplicationManager.getApplication().runWriteAction(runnable); - editor.getSelectionModel().removeSelection(); - } }, REFACTORING_NAME, null); } - @Override - public ExtractMethodInfoHelper getSettings(@NotNull InitialInfo initialInfo) { + private static void createMethod(ExtractMethodInfoHelper helper, GrMemberOwner owner, PsiElement startElement) { + final GrMethod method = ExtractUtil.createMethod(helper); + PsiElement anchor = calculateAnchorToInsertBefore(owner, startElement); + GrMethod newMethod = owner.addMemberDeclaration(method, anchor); + renameParameterOccurrences(newMethod, helper); + GrReferenceAdjuster.shortenReferences(newMethod); + PsiElement prev = newMethod.getPrevSibling(); + IElementType elementType = prev.getNode().getElementType(); + if (!TokenSets.WHITE_SPACES_SET.contains(elementType) || !prev.getText().contains("\n")) { + newMethod.getParent().getNode().addLeaf(GroovyTokenTypes.mNLS, "\n", newMethod.getNode()); + } + } + + @Nullable + private static ExtractMethodInfoHelper getSettings(@NotNull InitialInfo initialInfo, GrMemberOwner owner) { if (ApplicationManager.getApplication().isUnitTestMode()) { - final ExtractMethodInfoHelper helper = new ExtractMethodInfoHelper(initialInfo, "testMethod"); + final ExtractMethodInfoHelper helper = new ExtractMethodInfoHelper(initialInfo, "testMethod", owner); final PsiType type = helper.getOutputType(); - if (type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) || type.equalsToText("void")) { + if (type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) || PsiType.VOID.equals(type)) { helper.setSpecifyType(false); } return helper; } - GroovyExtractMethodDialog dialog = new GroovyExtractMethodDialog(initialInfo); + GroovyExtractMethodDialog dialog = new GroovyExtractMethodDialog(initialInfo, owner); dialog.show(); if (!dialog.isOK()) return null; @@ -166,4 +143,58 @@ public class GroovyExtractMethodHandler extends ExtractHandlerBase result = new ArrayList(); + if (!oldName.equals(newName)) { + for (final GrStatement statement : statements) { + statement.accept(new PsiRecursiveElementVisitor() { + public void visitElement(final PsiElement element) { + super.visitElement(element); + if (element instanceof GrReferenceExpression) { + GrReferenceExpression expr = (GrReferenceExpression) element; + if (!expr.isQualified() && oldName.equals(expr.getName())) { + result.add(expr); + } + } + } + }); + for (GrExpression expr : result) { + expr.replaceWithExpression(factory.createExpressionFromText(newName), false); + } + } + } + } + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/GrIntroduceContext.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/GrIntroduceContext.java index 184289237cb9..28b2a08cf93d 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/GrIntroduceContext.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/GrIntroduceContext.java @@ -1,9 +1,12 @@ /* - * Copyright 2000-2010 JetBrains s.r.o. + * Copyright 2000-2012 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. @@ -34,7 +37,7 @@ public class GrIntroduceContext { public GrIntroduceContext(Project project, Editor editor, - GrExpression expression, + @Nullable GrExpression expression, PsiElement[] occurrences, PsiElement scope, @Nullable GrVariable var) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/AnySupers.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/AnySupers.java index 46606a7df3e1..b9cdb2e1e309 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/AnySupers.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/AnySupers.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2012 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. @@ -21,7 +21,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrSuperR /** * @author Medvedev Max */ -class AnySupers extends GroovyRecursiveElementVisitor { +public class AnySupers extends GroovyRecursiveElementVisitor { boolean myContainsSupers = false; @Override @@ -30,7 +30,7 @@ class AnySupers extends GroovyRecursiveElementVisitor { myContainsSupers = true; } - boolean isResult() { + public boolean isResult() { return myContainsSupers; } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterProcessor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterProcessor.java index 52b0743f2b85..2f1314121c48 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterProcessor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2012 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. @@ -29,8 +29,6 @@ import com.intellij.refactoring.RefactoringBundle; import com.intellij.refactoring.introduceParameter.*; import com.intellij.refactoring.ui.UsageViewDescriptorAdapter; import com.intellij.refactoring.util.CommonRefactoringUtil; -import com.intellij.refactoring.util.ConflictsUtil; -import com.intellij.refactoring.util.RefactoringUIUtil; import com.intellij.refactoring.util.usageInfo.DefaultConstructorImplicitUsageInfo; import com.intellij.refactoring.util.usageInfo.NoConstructorClassUsageInfo; import com.intellij.usageView.UsageInfo; @@ -38,23 +36,14 @@ import com.intellij.usageView.UsageViewDescriptor; import com.intellij.usageView.UsageViewUtil; import com.intellij.util.containers.MultiMap; import gnu.trove.TIntArrayList; -import gnu.trove.TIntProcedure; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; -import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; -import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureSignature; -import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil; -import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils; import java.util.ArrayList; import java.util.Collection; -import java.util.List; /** * @author Maxim.Medvedev @@ -99,7 +88,10 @@ public class GrIntroduceParameterProcessor extends BaseRefactoringProcessor impl MultiMap conflicts = new MultiMap(); if (!mySettings.generateDelegate()) { - detectAccessibilityConflicts(usagesIn, conflicts); + GroovyIntroduceParameterUtil.detectAccessibilityConflicts(myContext.expression, usagesIn, conflicts, + mySettings.replaceFieldsWithGetters() != IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_NONE, + myProject + ); } final GrMethod toReplaceIn = (GrMethod)myContext.toReplaceIn; @@ -127,41 +119,6 @@ public class GrIntroduceParameterProcessor extends BaseRefactoringProcessor impl return showConflicts(conflicts, usagesIn); } - private void detectAccessibilityConflicts(final UsageInfo[] usageArray, MultiMap conflicts) { - if (myContext.expression == null) return; - - final ReferencedElementsCollector collector = new ReferencedElementsCollector(); - myContext.expression.accept(collector); - final List result = collector.getResult(); - if (result.isEmpty()) return; - - for (final UsageInfo usageInfo : usageArray) { - if (!(usageInfo instanceof ExternalUsageInfo) || !IntroduceParameterUtil.isMethodUsage(usageInfo)) continue; - - final PsiElement place = usageInfo.getElement(); - for (PsiElement element : result) { - if (element instanceof PsiField && - mySettings.replaceFieldsWithGetters() != IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_NONE) { - //check getter access instead - final PsiClass psiClass = ((PsiField)element).getContainingClass(); - LOG.assertTrue(psiClass != null); - final PsiMethod method = GroovyPropertyUtils.findGetterForField((PsiField)element); - if (method != null) { - element = method; - } - } - if (element instanceof PsiMember && - !JavaPsiFacade.getInstance(myProject).getResolveHelper().isAccessible((PsiMember)element, place, null)) { - String message = RefactoringBundle.message( - "0.is.not.accessible.from.1.value.for.introduced.parameter.in.that.method.call.will.be.incorrect", - RefactoringUIUtil.getDescription(element, true), - RefactoringUIUtil.getDescription(ConflictsUtil.getContainer(place), true)); - conflicts.putValue(element, message); - } - } - } - } - @NotNull @Override protected UsageInfo[] findUsages() { @@ -220,7 +177,7 @@ public class GrIntroduceParameterProcessor extends BaseRefactoringProcessor impl PsiType initializerType = mySettings.getSelectedType(); - // Changing external occurences (the tricky part) + // Changing external occurrences (the tricky part) IntroduceParameterUtil.processUsages(usages, this); @@ -229,9 +186,9 @@ public class GrIntroduceParameterProcessor extends BaseRefactoringProcessor impl final boolean methodsToProcessAreDifferent = toReplaceIn != toSearchFor; if (mySettings.generateDelegate()) { - generateDelegate(toReplaceIn); + GroovyIntroduceParameterUtil.generateDelegate(toReplaceIn, myParameterInitializer, myProject); if (methodsToProcessAreDifferent) { - final GrMethod method = generateDelegate(toSearchFor); + final GrMethod method = GroovyIntroduceParameterUtil.generateDelegate(toSearchFor, myParameterInitializer, myProject); final PsiClass containingClass = method.getContainingClass(); if (containingClass != null && containingClass.isInterface()) { final GrOpenBlock block = method.getBlock(); @@ -255,12 +212,12 @@ public class GrIntroduceParameterProcessor extends BaseRefactoringProcessor impl if (myContext.var != null) myContext.var.delete(); - // Replacing expression occurences + // Replacing expression occurrences for (UsageInfo usage : usages) { if (usage instanceof ChangedMethodCallInfo) { PsiElement element = usage.getElement(); - processChangedMethodCall(element); + GroovyIntroduceParameterUtil.processChangedMethodCall(element, mySettings, myProject); } else if (usage instanceof InternalUsageInfo) { PsiElement element = usage.getElement(); @@ -281,86 +238,6 @@ public class GrIntroduceParameterProcessor extends BaseRefactoringProcessor impl fieldConflictsResolver.fix(); } - private GrMethod generateDelegate(PsiMethod prototype) { - final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(myProject); - - GrMethod result; - if (prototype instanceof GrMethod) { - result = (GrMethod)prototype.copy(); - } - else { - StringBuilder builder = new StringBuilder(); - builder.append(prototype.getModifierList().getText()).append(' '); - - if (prototype.getReturnTypeElement() != null ) { - builder.append(prototype.getReturnTypeElement().getText()); - } - builder.append(' ').append(prototype.getName()); - builder.append(prototype.getParameterList().getText()); - builder.append("{}"); - result = factory.createMethodFromText(builder.toString()); - } - - StringBuilder call = new StringBuilder(); - call.append("def foo(){\n").append(prototype.getName()).append('('); - final GrParameter[] parameters = result.getParameters(); - for (GrParameter parameter : parameters) { - call.append(parameter.getName()).append(", "); - } - call.append(myParameterInitializer.getText()); - call.append(");\n}"); - final GrOpenBlock block = factory.createMethodFromText(call.toString()).getBlock(); - - result.getBlock().replace(block); - final PsiElement parent = prototype.getParent(); - return (GrMethod)parent.addBefore(result, prototype); - } - - private void processChangedMethodCall(PsiElement element) { - if (element.getParent() instanceof GrMethodCallExpression) { - GrMethodCallExpression methodCall = (GrMethodCallExpression)element.getParent(); - - GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(myProject); - GrExpression expression = factory.createExpressionFromText(mySettings.getName(), null); - final GrArgumentList argList = methodCall.getArgumentList(); - final PsiElement[] exprs = argList.getAllArguments(); - - if (exprs.length > 0) { - argList.addAfter(expression, exprs[exprs.length - 1]); - } - else { - argList.add(expression); - } - - removeParametersFromCall(methodCall, argList); - - } - else { - LOG.error(element.getParent()); - } - - } - - private void removeParametersFromCall(GrMethodCallExpression methodCall, GrArgumentList argList) { - final GroovyResolveResult resolveResult = methodCall.advancedResolve(); - final PsiElement resolved = resolveResult.getElement(); - LOG.assertTrue(resolved instanceof PsiMethod); - final GrClosureSignature signature = GrClosureSignatureUtil.createSignature((PsiMethod)resolved, resolveResult.getSubstitutor()); - final GrClosureSignatureUtil.ArgInfo[] argInfos = - GrClosureSignatureUtil.mapParametersToArguments(signature, argList, methodCall, methodCall.getClosureArguments()); - LOG.assertTrue(argInfos != null); - mySettings.parametersToRemove().forEach(new TIntProcedure() { - @Override - public boolean execute(int value) { - final List args = argInfos[value].args; - for (PsiElement arg : args) { - arg.delete(); - } - return true; - } - }); - } - @Override protected String getCommandName() { return RefactoringBundle.message("introduce.parameter.command", UsageViewUtil.getDescriptiveName(myContext.toReplaceIn)); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GroovyIntroduceParameterUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GroovyIntroduceParameterUtil.java index 243aca27d665..8cd35f39f01b 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GroovyIntroduceParameterUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GroovyIntroduceParameterUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2012 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. @@ -16,26 +16,44 @@ package org.jetbrains.plugins.groovy.refactoring.introduce.parameter; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.refactoring.RefactoringBundle; +import com.intellij.refactoring.introduceParameter.ExternalUsageInfo; +import com.intellij.refactoring.introduceParameter.IntroduceParameterData; +import com.intellij.refactoring.introduceParameter.IntroduceParameterUtil; +import com.intellij.refactoring.util.ConflictsUtil; +import com.intellij.refactoring.util.RefactoringUIUtil; +import com.intellij.usageView.UsageInfo; import com.intellij.util.IncorrectOperationException; import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.MultiMap; import gnu.trove.TIntArrayList; import gnu.trove.TIntProcedure; import gnu.trove.TObjectIntHashMap; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.lang.GrReferenceAdjuster; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrParametersOwner; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrThisReferenceExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; +import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureSignature; import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil; import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils; @@ -156,6 +174,143 @@ public class GroovyIntroduceParameterUtil { }); } + public static void detectAccessibilityConflicts(@Nullable GroovyPsiElement elementToProcess, + final UsageInfo[] usages, + MultiMap conflicts, + boolean replaceFieldsWithGetters, + Project project) { + if (elementToProcess == null) return; + + final ReferencedElementsCollector collector = new ReferencedElementsCollector(); + elementToProcess.accept(collector); + final List result = collector.getResult(); + if (result.isEmpty()) return; + + for (final UsageInfo usageInfo : usages) { + if (!(usageInfo instanceof ExternalUsageInfo) || !IntroduceParameterUtil.isMethodUsage(usageInfo)) continue; + + final PsiElement place = usageInfo.getElement(); + for (PsiElement element : result) { + if (element instanceof PsiField && replaceFieldsWithGetters) { + //check getter access instead + final PsiClass psiClass = ((PsiField)element).getContainingClass(); + LOG.assertTrue(psiClass != null); + final PsiMethod method = GroovyPropertyUtils.findGetterForField((PsiField)element); + if (method != null) { + element = method; + } + } + if (element instanceof PsiMember && + !JavaPsiFacade.getInstance(project).getResolveHelper().isAccessible((PsiMember)element, place, null)) { + String message = RefactoringBundle.message( + "0.is.not.accessible.from.1.value.for.introduced.parameter.in.that.method.call.will.be.incorrect", + RefactoringUIUtil.getDescription(element, true), + RefactoringUIUtil.getDescription(ConflictsUtil.getContainer(place), true)); + conflicts.putValue(element, message); + } + } + } + } + + public static void processChangedMethodCall(PsiElement element, GrIntroduceParameterSettings settings, Project project) { + if (!(element.getParent() instanceof GrMethodCallExpression)) { + LOG.error(element.getParent()); + return; + } + + GrMethodCallExpression methodCall = (GrMethodCallExpression)element.getParent(); + + GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(project); + final String name = settings.getName(); + LOG.assertTrue(name != null); + GrExpression expression = factory.createExpressionFromText(name, null); + final GrArgumentList argList = methodCall.getArgumentList(); + final PsiElement[] exprs = argList.getAllArguments(); + + if (exprs.length > 0) { + argList.addAfter(expression, exprs[exprs.length - 1]); + } + else { + argList.add(expression); + } + + removeParametersFromCall(methodCall, argList, settings); + } + + private static void removeParametersFromCall(GrMethodCallExpression methodCall, + GrArgumentList argList, + GrIntroduceParameterSettings settings) { + final GroovyResolveResult resolveResult = methodCall.advancedResolve(); + final PsiElement resolved = resolveResult.getElement(); + LOG.assertTrue(resolved instanceof PsiMethod); + final GrClosureSignature signature = GrClosureSignatureUtil.createSignature((PsiMethod)resolved, resolveResult.getSubstitutor()); + final GrClosureSignatureUtil.ArgInfo[] argInfos = + GrClosureSignatureUtil.mapParametersToArguments(signature, argList, methodCall, methodCall.getClosureArguments()); + LOG.assertTrue(argInfos != null); + settings.parametersToRemove().forEach(new TIntProcedure() { + @Override + public boolean execute(int value) { + final List args = argInfos[value].args; + for (PsiElement arg : args) { + arg.delete(); + } + return true; + } + }); + } + + public static GrMethod generateDelegate(PsiMethod prototype, IntroduceParameterData.ExpressionWrapper initializer, Project project) { + final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(project); + + GrMethod result; + if (prototype instanceof GrMethod) { + result = (GrMethod)prototype.copy(); + } + else { + StringBuilder builder = new StringBuilder(); + builder.append(prototype.getModifierList().getText()).append(' '); + + if (prototype.getReturnTypeElement() != null ) { + builder.append(prototype.getReturnTypeElement().getText()); + } + builder.append(' ').append(prototype.getName()); + builder.append(prototype.getParameterList().getText()); + builder.append("{}"); + result = factory.createMethodFromText(builder.toString()); + } + + StringBuilder call = new StringBuilder(); + call.append("def foo(){\n"); + final GrParameter[] parameters = result.getParameters(); + call.append(prototype.getName()); + if (initializer.getExpression() instanceof GrClosableBlock) { + if (parameters.length > 0) { + call.append('('); + for (GrParameter parameter : parameters) { + call.append(parameter.getName()).append(", "); + } + call.replace(call.length()-2, call.length(), ")"); + } + call.append(initializer.getText()); + } + else { + call.append('('); + for (GrParameter parameter : parameters) { + call.append(parameter.getName()).append(", "); + } + call.append(initializer.getText()); + call.append(")"); + } + call.append("\n}"); + final GrOpenBlock block = factory.createMethodFromText(call.toString()).getBlock(); + + result.getBlock().replace(block); + final PsiElement parent = prototype.getParent(); + final GrMethod method = (GrMethod)parent.addBefore(result, prototype); + GrReferenceAdjuster.shortenReferences(method); + return method; + } + private static class FieldSearcher extends GroovyRecursiveElementVisitor { PsiClass myClass; private final List result = new ArrayList(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/java2groovy/OldReferencesResolver.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/java2groovy/OldReferencesResolver.java index e0ab628ba4b5..13059b33361a 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/java2groovy/OldReferencesResolver.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/java2groovy/OldReferencesResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2012 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. @@ -52,8 +52,7 @@ import static com.intellij.codeInsight.ChangeContextUtil.*; */ public class OldReferencesResolver { - private static final Logger LOG = - Logger.getInstance("#org.jetbrains.plugins.groovy.refactoring.introduce.parameter.java2groovy.OldReferencesResolver"); + private static final Logger LOG = Logger.getInstance(OldReferencesResolver.class); private final GrCall myContext; private final GrExpression myExpr; diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureTest.groovy new file mode 100644 index 000000000000..07af99ebf109 --- /dev/null +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/extract/closure/ExtractClosureTest.groovy @@ -0,0 +1,156 @@ +/* + * Copyright 2000-2012 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.plugins.groovy.refactoring.extract.closure; + + +import com.intellij.psi.PsiElement +import gnu.trove.TIntArrayList +import org.jetbrains.annotations.NotNull +import org.jetbrains.plugins.groovy.LightGroovyTestCase +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrParametersOwner +import org.jetbrains.plugins.groovy.refactoring.extract.InitialInfo +import org.jetbrains.plugins.groovy.util.TestUtils + +/** + * @author Max Medvedev + */ +public class ExtractClosureTest extends LightGroovyTestCase { + @Override + protected String getBasePath() { + return "${TestUtils.testDataPath}groovy/refactoring/extractMethod/"; + } + + + private void doTest(String before, String after) { + doTest(before, after, [], []) + } + + private void doTest(String before, String after, List toRemove, List notToUseAsParams) { + myFixture.configureByText 'a.groovy', before + def model = myFixture.editor.selectionModel + def handler = new ExtractClosureHandler() { + @Override + protected ExtractClosureHelper getSettings(@NotNull InitialInfo initialInfo, GrParametersOwner owner, PsiElement toSearchFor) { + def settings = new ExtractClosureHelper(initialInfo, owner, toSearchFor, "closure", true) + settings.setDeclareFinal(false) + settings.setGenerateDelegate(false) + settings.setName("closure") + settings.setToRemove(new TIntArrayList(toRemove as int[])) + + def infos = settings.parameterInfos + for (int i: notToUseAsParams) { + infos[i].setPassAsParameter(false) + } + return settings + } + } + + handler.invoke myFixture.project, myFixture.editor, myFixture.file, model.selectionStart, model.selectionEnd + myFixture.checkResult after + } + + + void testSimple() { + doTest(''' +def foo(String s) { + s+=2 + print s +} + +foo('a') +''', ''' +def foo(String s, Closure closure) { + s+=2 + closure(s) +} + +foo('a') {String s -> + print s +} +''') + } + + void testRemoveUnused() { + doTest(''' +class X { + def foo(String s) { + print s + } +} + +new X().foo('a') +''', ''' +class X { + def foo(Closure closure) { + closure() + } +} + +new X().foo {-> + print 'a' +} +''', [0], [0]) + + } + + void testRemoveUnusedAndGenerateLocal() { + doTest(''' +def foo(String s) { + s+=2 + print s +} + +foo('a') +''', ''' +def foo(Closure closure) { + closure() +} + +foo {-> + String s = 'a' + s += 2 + print s +} +''', [0], [0]) + + } + + void testInsertQualifier() { + doTest(''' +class X { + def foo(String s) { + bar() + } + def bar(){} +} + +new X().foo('a') +''', ''' +class X { + def foo(Closure closure) { + closure() + } + def bar(){} +} + +final X x = new X() +x.foo {-> + x.bar() +} +''', [0], []) + + } +} diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/extract/method/ExtractMethodTest.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/extract/method/ExtractMethodTest.java index ca9a658db497..8cf703b51cd8 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/extract/method/ExtractMethodTest.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/extract/method/ExtractMethodTest.java @@ -106,5 +106,6 @@ public class ExtractMethodTest extends LightGroovyTestCase { public void testAOOBE() {doTest();} public void testWildCardReturnType() {doTest();} + public void testParamChangedInsideExtractedMethod() {doTest();} } \ No newline at end of file diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/introduceParameter/GrIntroduceParameterTest.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/introduceParameter/GrIntroduceParameterTest.java index 446bd52004d4..fc60dffd3b1a 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/introduceParameter/GrIntroduceParameterTest.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/introduceParameter/GrIntroduceParameterTest.java @@ -333,4 +333,5 @@ public class GrIntroduceParameterTest extends LightCodeInsightFixtureTestCase { public void testClosureArg() {doTest(IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_NONE, false, false);} public void testClosureArgWithEmptyArgList() {doTest(IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_NONE, true, false);} + public void testScriptMethod() {doTest(IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_NONE, true, false);} } diff --git a/plugins/groovy/testdata/groovy/refactoring/extractMethod/inter1.test b/plugins/groovy/testdata/groovy/refactoring/extractMethod/inter1.test index 7e98291048d8..3194f14f5856 100644 --- a/plugins/groovy/testdata/groovy/refactoring/extractMethod/inter1.test +++ b/plugins/groovy/testdata/groovy/refactoring/extractMethod/inter1.test @@ -15,7 +15,7 @@ def i = 6 i = testMethod() private int testMethod() { - int i + def i while (true) { println "preved" i = 42 diff --git a/plugins/groovy/testdata/groovy/refactoring/extractMethod/meth_em3.test b/plugins/groovy/testdata/groovy/refactoring/extractMethod/meth_em3.test index 76651aa31c70..2a75d8f62a7f 100644 --- a/plugins/groovy/testdata/groovy/refactoring/extractMethod/meth_em3.test +++ b/plugins/groovy/testdata/groovy/refactoring/extractMethod/meth_em3.test @@ -11,7 +11,7 @@ protected static def getGeneratedFileNames(String name, int boo) { } private static ArrayList testMethod() { - ArrayList names + def names names = new ArrayList() return names } \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/refactoring/extractMethod/paramChangedInsideExtractedMethod.test b/plugins/groovy/testdata/groovy/refactoring/extractMethod/paramChangedInsideExtractedMethod.test new file mode 100644 index 000000000000..06aa4dc8c045 --- /dev/null +++ b/plugins/groovy/testdata/groovy/refactoring/extractMethod/paramChangedInsideExtractedMethod.test @@ -0,0 +1,14 @@ +def foo(String s){ + s = 'a' + print s +} +----- +def foo(String s){ + testMethod() +} + +private testMethod() { + String s + s = 'a' + print s +} diff --git a/plugins/groovy/testdata/refactoring/introduceParameterGroovy/delegate1/Delegate1MyClass_after.groovy b/plugins/groovy/testdata/refactoring/introduceParameterGroovy/delegate1/Delegate1MyClass_after.groovy index 09825055dd40..30aa351c71f4 100644 --- a/plugins/groovy/testdata/refactoring/introduceParameterGroovy/delegate1/Delegate1MyClass_after.groovy +++ b/plugins/groovy/testdata/refactoring/introduceParameterGroovy/delegate1/Delegate1MyClass_after.groovy @@ -1,5 +1,20 @@ +/* + * Copyright 2000-2012 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. + */ def foo() { - foo("foo"); + foo("foo") } def foo(String anObject) { diff --git a/plugins/groovy/testdata/refactoring/introduceParameterGroovy/delegate2/Delegate2MyClass_after.groovy b/plugins/groovy/testdata/refactoring/introduceParameterGroovy/delegate2/Delegate2MyClass_after.groovy index 4b60354d4304..a610ba8aebe5 100644 --- a/plugins/groovy/testdata/refactoring/introduceParameterGroovy/delegate2/Delegate2MyClass_after.groovy +++ b/plugins/groovy/testdata/refactoring/introduceParameterGroovy/delegate2/Delegate2MyClass_after.groovy @@ -1,5 +1,20 @@ +/* + * Copyright 2000-2012 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. + */ def foo(def p) { - foo(p, "foo"); + foo(p, "foo") } def foo(def p, String anObject) { diff --git a/plugins/groovy/testdata/refactoring/introduceParameterGroovy/delegaterInSuper/DelegaterInSuperMyClass_after.groovy b/plugins/groovy/testdata/refactoring/introduceParameterGroovy/delegaterInSuper/DelegaterInSuperMyClass_after.groovy index aa6dbf5bd5d4..1f64cc5f64b1 100644 --- a/plugins/groovy/testdata/refactoring/introduceParameterGroovy/delegaterInSuper/DelegaterInSuperMyClass_after.groovy +++ b/plugins/groovy/testdata/refactoring/introduceParameterGroovy/delegaterInSuper/DelegaterInSuperMyClass_after.groovy @@ -1,6 +1,21 @@ +/* + * Copyright 2000-2012 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. + */ class Base { def foo() { - foo(123); + foo(123) } def foo(int anObject){} @@ -8,7 +23,7 @@ class Base { class Inh extends Base { def foo() { - foo(123); + foo(123) } def foo(int anObject) {print anObject} diff --git a/plugins/groovy/testdata/refactoring/introduceParameterGroovy/scriptMethod/ScriptMethodMyClass.groovy b/plugins/groovy/testdata/refactoring/introduceParameterGroovy/scriptMethod/ScriptMethodMyClass.groovy new file mode 100644 index 000000000000..55489d4c13ff --- /dev/null +++ b/plugins/groovy/testdata/refactoring/introduceParameterGroovy/scriptMethod/ScriptMethodMyClass.groovy @@ -0,0 +1,20 @@ +/* + * Copyright 2000-2012 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. + */ +def foo(String s) { + print(s + 1) +} + +foo('a') \ No newline at end of file diff --git a/plugins/groovy/testdata/refactoring/introduceParameterGroovy/scriptMethod/ScriptMethodMyClass_after.groovy b/plugins/groovy/testdata/refactoring/introduceParameterGroovy/scriptMethod/ScriptMethodMyClass_after.groovy new file mode 100644 index 000000000000..4b33be5d7ffe --- /dev/null +++ b/plugins/groovy/testdata/refactoring/introduceParameterGroovy/scriptMethod/ScriptMethodMyClass_after.groovy @@ -0,0 +1,20 @@ +/* + * Copyright 2000-2012 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. + */ +def foo(def anObject) { + print(anObject) +} + +foo('a' + 1) \ No newline at end of file