blob: 0c71f2c0f93ef15e1f73aa8b30d8713303b5c485 [file] [log] [blame]
/*
* Copyright 2000-2014 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.intentions.closure;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.intentions.base.Intention;
import org.jetbrains.plugins.groovy.intentions.base.PsiElementPredicate;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifier;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.*;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
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.clauses.GrForClause;
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.GrMethodCallExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameterList;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnonymousClassDefinition;
import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
import java.util.Set;
/**
* @author Maxim.Medvedev
*/
public class EachToForIntention extends Intention {
@NonNls public static final String OUTER = "Outer";
@NonNls public static final String HINT = "Replace with For-In";
@NotNull
@Override
protected PsiElementPredicate getElementPredicate() {
return new EachToForPredicate();
}
@Override
protected void processIntention(@NotNull PsiElement element, Project project, Editor editor) throws IncorrectOperationException {
final GrMethodCallExpression expression = (GrMethodCallExpression)element;
final GrClosableBlock block = expression.getClosureArguments()[0];
final GrParameterList parameterList = block.getParameterList();
final GrParameter[] parameters = parameterList.getParameters();
String var;
if (parameters.length == 1) {
var = parameters[0].getText();
var = StringUtil.replace(var, GrModifier.DEF, "");
}
else {
var = "it";
}
final GrExpression invokedExpression = expression.getInvokedExpression();
GrExpression qualifier = ((GrReferenceExpression)invokedExpression).getQualifierExpression();
final GroovyPsiElementFactory elementFactory = GroovyPsiElementFactory.getInstance(element.getProject());
if (qualifier == null) {
qualifier = elementFactory.createExpressionFromText("this");
}
StringBuilder builder = new StringBuilder();
builder.append("for (").append(var).append(" in ").append(qualifier.getText()).append(") {\n");
String text = block.getText();
final PsiElement blockArrow = block.getArrow();
int index;
if (blockArrow != null) {
index = blockArrow.getStartOffsetInParent() + blockArrow.getTextLength();
}
else {
index = 1;
}
while (index < text.length() && Character.isWhitespace(text.charAt(index))) index++;
text = text.substring(index, text.length() - 1);
builder.append(text);
builder.append("}");
final GrStatement statement = elementFactory.createStatementFromText(builder.toString());
GrForStatement forStatement = (GrForStatement)expression.replaceWithStatement(statement);
final GrForClause clause = forStatement.getClause();
GrVariable variable = clause.getDeclaredVariable();
forStatement = updateReturnStatements(forStatement);
if (variable == null) return;
if (ApplicationManager.getApplication().isUnitTestMode()) return;
final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
final Document doc = documentManager.getDocument(element.getContainingFile());
if (doc == null) return;
documentManager.doPostponedOperationsAndUnblockDocument(doc);
editor.getCaretModel().moveToOffset(variable.getTextOffset());
new VariableInplaceRenamer(variable, editor).performInplaceRename();
}
private static GrForStatement updateReturnStatements(GrForStatement forStatement) {
GrStatement body = forStatement.getBody();
assert body != null;
final Set<String> usedLabels = ContainerUtil.newHashSet();
final Ref<Boolean> needLabel = Ref.create(false);
body.accept(new GroovyRecursiveElementVisitor() {
private int myLoops = 0;
@Override
public void visitReturnStatement(GrReturnStatement returnStatement) {
if (returnStatement.getReturnValue() != null) return;
if (myLoops > 0) needLabel.set(true);
}
@Override
public void visitLabeledStatement(GrLabeledStatement labeledStatement) {
super.visitLabeledStatement(labeledStatement);
usedLabels.add(labeledStatement.getName());
}
@Override
public void visitForStatement(GrForStatement forStatement) {
myLoops++;
super.visitForStatement(forStatement);
myLoops--;
}
@Override
public void visitWhileStatement(GrWhileStatement whileStatement) {
myLoops++;
super.visitWhileStatement(whileStatement);
myLoops--;
}
@Override
public void visitClosure(GrClosableBlock closure) {
//don't go into closures
}
@Override
public void visitAnonymousClassDefinition(GrAnonymousClassDefinition anonymousClassDefinition) {
//don't go into anonymous
}
});
GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(forStatement.getProject());
final String continueText;
if (needLabel.get()) {
int i = 0;
String label = OUTER;
while (usedLabels.contains(label)) {
label = OUTER + i;
i++;
}
continueText = "continue "+ label;
GrLabeledStatement labeled = (GrLabeledStatement)factory.createStatementFromText(label + ": while (true){}");
labeled.getStatement().replaceWithStatement(forStatement);
labeled = forStatement.replaceWithStatement(labeled);
forStatement = (GrForStatement)labeled.getStatement();
body = forStatement.getBody();
assert body != null;
}
else {
continueText = "continue";
}
final GrStatement continueStatement = factory.createStatementFromText(continueText);
body.accept(new GroovyRecursiveElementVisitor() {
@Override
public void visitReturnStatement(GrReturnStatement returnStatement) {
if (returnStatement.getReturnValue() == null) {
returnStatement.replaceWithStatement(continueStatement);
}
}
@Override
public void visitClosure(GrClosableBlock closure) {
//don't go into closures
}
@Override
public void visitAnonymousClassDefinition(GrAnonymousClassDefinition anonymousClassDefinition) {
//don't go into anonymous
}
});
return forStatement;
}
private static class EachToForPredicate implements PsiElementPredicate {
@Override
public boolean satisfiedBy(PsiElement element) {
if (element instanceof GrMethodCallExpression) {
final GrMethodCallExpression expression = (GrMethodCallExpression)element;
// final PsiElement parent = expression.getParent();
// if (parent instanceof GrAssignmentExpression) return false;
// if (parent instanceof GrArgumentList) return false;
// if (parent instanceof GrReturnStatement) return false;
// if (!(parent instanceof GrCodeBlock || parent instanceof GrIfStatement|| parent instanceof GrCaseSection)) return false;
final GrExpression invokedExpression = expression.getInvokedExpression();
if (invokedExpression instanceof GrReferenceExpression) {
GrReferenceExpression referenceExpression = (GrReferenceExpression)invokedExpression;
if ("each".equals(referenceExpression.getReferenceName())) {
final GrArgumentList argumentList = expression.getArgumentList();
if (argumentList != null) {
if (PsiImplUtil.hasExpressionArguments(argumentList)) return false;
if (PsiImplUtil.hasNamedArguments(argumentList)) return false;
}
final GrClosableBlock[] closureArguments = expression.getClosureArguments();
if (closureArguments.length != 1) return false;
final GrParameter[] parameters = closureArguments[0].getParameterList().getParameters();
if (parameters.length > 1) return false;
return true;
}
}
}
return false;
}
}
}