diff --git a/plugins/groovy/resources/intentionDescriptions/ReplaceIfWithTernaryIntention/after.groovy.template b/plugins/groovy/resources/intentionDescriptions/ReplaceIfWithTernaryIntention/after.groovy.template new file mode 100644 index 000000000000..0f413f682b3d --- /dev/null +++ b/plugins/groovy/resources/intentionDescriptions/ReplaceIfWithTernaryIntention/after.groovy.template @@ -0,0 +1 @@ +return cond() : first() : second() \ No newline at end of file diff --git a/plugins/groovy/resources/intentionDescriptions/ReplaceIfWithTernaryIntention/before.groovy.template b/plugins/groovy/resources/intentionDescriptions/ReplaceIfWithTernaryIntention/before.groovy.template new file mode 100644 index 000000000000..c9bf0d66b7d4 --- /dev/null +++ b/plugins/groovy/resources/intentionDescriptions/ReplaceIfWithTernaryIntention/before.groovy.template @@ -0,0 +1,6 @@ +if (cond()) { + return first() +} +else { + return second() +} \ No newline at end of file diff --git a/plugins/groovy/resources/intentionDescriptions/ReplaceIfWithTernaryIntention/description.html b/plugins/groovy/resources/intentionDescriptions/ReplaceIfWithTernaryIntention/description.html new file mode 100644 index 000000000000..e7e00ed842d9 --- /dev/null +++ b/plugins/groovy/resources/intentionDescriptions/ReplaceIfWithTernaryIntention/description.html @@ -0,0 +1,7 @@ + + +This intention replaces if-statement with equivalent ternary operator + +Text after this comment will not be shown in tooltips. + + \ No newline at end of file diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index 3fe8e1036cbf..c214cd7266aa 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -1019,6 +1019,11 @@ intention.category.groovy/intention.category.control.flow org.jetbrains.plugins.groovy.intentions.control.ReplaceTernaryWithIfElseIntention + + org.jetbrains.plugins.groovy.intentions.GroovyIntentionsBundle + intention.category.groovy/intention.category.control.flow + org.jetbrains.plugins.groovy.intentions.control.ReplaceIfWithTernaryIntention + org.jetbrains.plugins.groovy.intentions.GroovyIntentionsBundle intention.category.groovy/intention.category.control.flow diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/GroovyIntentionsBundle.properties b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/GroovyIntentionsBundle.properties index 61fc76ce81fe..6843488e0f99 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/GroovyIntentionsBundle.properties +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/GroovyIntentionsBundle.properties @@ -198,4 +198,6 @@ cannot.create.class=Cannot create class gr.introduce.local.variable.intention.name=Introduce local variable gr.introduce.local.variable.intention.family.name=Groovy Introduce local variable flip.if.intention.name=Flip if statements -flip.if.intention.family.name=Flip if statements \ No newline at end of file +flip.if.intention.family.name=Flip if statements +replace.if.with.ternary.intention.name=Replace with ?: +replace.if.with.ternary.intention.family.name=Replace if-statement with ternary operator \ No newline at end of file diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/control/ReplaceIfWithTernaryIntention.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/control/ReplaceIfWithTernaryIntention.java new file mode 100644 index 000000000000..1ea8ce2d681a --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/control/ReplaceIfWithTernaryIntention.java @@ -0,0 +1,117 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.plugins.groovy.intentions.control; + +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.codeInspection.utils.EquivalenceChecker; +import org.jetbrains.plugins.groovy.intentions.base.Intention; +import org.jetbrains.plugins.groovy.intentions.base.PsiElementPredicate; +import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrBlockStatement; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrIfStatement; +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.GrConditionalExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; + +/** + * @author Max Medvedev + */ +public class ReplaceIfWithTernaryIntention extends Intention { + @Override + protected void processIntention(@NotNull PsiElement element, Project project, Editor editor) throws IncorrectOperationException { + final GrIfStatement ifStatement = (GrIfStatement)element.getParent(); + + final PsiElement thenBranch = skipBlock(ifStatement.getThenBranch()); + final PsiElement elseBranch = skipBlock(ifStatement.getElseBranch()); + + if (thenBranch instanceof GrAssignmentExpression && elseBranch instanceof GrAssignmentExpression) { + final GrAssignmentExpression assignment = (GrAssignmentExpression)GroovyPsiElementFactory.getInstance(project).createStatementFromText("a = b ? c : d"); + + assignment.getLValue().replaceWithExpression(((GrAssignmentExpression)thenBranch).getLValue(), true); + + final GrConditionalExpression conditional = (GrConditionalExpression)assignment.getRValue(); + replaceConditional(conditional, ifStatement.getCondition(), ((GrAssignmentExpression)thenBranch).getRValue(), ((GrAssignmentExpression)elseBranch).getRValue()); + ifStatement.replaceWithStatement(assignment); + } + + + if (thenBranch instanceof GrReturnStatement && elseBranch instanceof GrReturnStatement) { + final GrReturnStatement returnSt = (GrReturnStatement)GroovyPsiElementFactory.getInstance(project).createStatementFromText("return a ? b : c"); + final GrConditionalExpression conditional = (GrConditionalExpression)returnSt.getReturnValue(); + replaceConditional(conditional, ifStatement.getCondition(), ((GrReturnStatement)thenBranch).getReturnValue(), ((GrReturnStatement)elseBranch).getReturnValue()); + + ifStatement.replaceWithStatement(returnSt); + } + } + + @SuppressWarnings("ConstantConditions") + private static void replaceConditional(GrConditionalExpression conditional, + GrExpression condition, + GrExpression then, + GrExpression elze) { + conditional.getCondition().replaceWithExpression(condition, true); + conditional.getThenBranch().replaceWithExpression(then, true); + conditional.getElseBranch().replaceWithExpression(elze, true); + } + + @NotNull + @Override + protected PsiElementPredicate getElementPredicate() { + return new PsiElementPredicate() { + @Override + public boolean satisfiedBy(PsiElement e) { + if (!e.getNode().getElementType().equals(GroovyTokenTypes.kIF)) return false; + + final GrIfStatement ifStatement = (GrIfStatement)e.getParent(); + final PsiElement thenBranch = skipBlock(ifStatement.getThenBranch()); + final PsiElement elseBranch = skipBlock(ifStatement.getElseBranch()); + + if (thenBranch instanceof GrAssignmentExpression && + elseBranch instanceof GrAssignmentExpression && + ((GrAssignmentExpression)thenBranch).getRValue() != null && + ((GrAssignmentExpression)elseBranch).getRValue() != null) { + final GrExpression lvalue1 = ((GrAssignmentExpression)thenBranch).getLValue(); + final GrExpression lvalue2 = ((GrAssignmentExpression)elseBranch).getLValue(); + return EquivalenceChecker.expressionsAreEquivalent(lvalue1, lvalue2); + } + + if (thenBranch instanceof GrReturnStatement && + elseBranch instanceof GrReturnStatement && + ((GrReturnStatement)thenBranch).getReturnValue() != null && + ((GrReturnStatement)elseBranch).getReturnValue() != null) { + return true; + } + + return false; + } + }; + } + + private static PsiElement skipBlock(PsiElement e) { + if (e instanceof GrBlockStatement && ((GrBlockStatement)e).getBlock().getStatements().length == 1) { + return ((GrBlockStatement)e).getBlock().getStatements()[0]; + } + else { + return e; + } + } +} diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/intentions/ReplaceifWithTernaryTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/intentions/ReplaceifWithTernaryTest.groovy new file mode 100644 index 000000000000..034db1e55d02 --- /dev/null +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/intentions/ReplaceifWithTernaryTest.groovy @@ -0,0 +1,72 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.plugins.groovy.intentions + +/** + * @author Max Medvedev + */ +class ReplaceIfWithTernaryTest extends GrIntentionTestCase { + ReplaceIfWithTernaryTest() { + super('Replace with ?:') + } + + public void testReturn() throws Exception { + doTextTest('''\ +def foo() { + if (cond()) { + return 'a' + } + else return 'b' +} +''', '''\ +def foo() { + return cond() ? 'a' : 'b' +} +''') + } + + public void testAssign() throws Exception { + doTextTest('''\ +def a + +if (cond()) { + a = 'a' +} +else { + a = 'b' +} +''', '''\ +def a + +a = cond() ? 'a' : 'b' +''') + } + + public void testAntiAssign() throws Exception { + doAntiTest('''\ +def a +def b + +if (cond) { + a = 'a' +} +else { + b = 'b' +} +''') + } + +}