diff --git a/plugins/groovy/resources/intentionDescriptions/SimplifyTernaryOperatorIntention/after.groovy.template b/plugins/groovy/resources/intentionDescriptions/SimplifyTernaryOperatorIntention/after.groovy.template
new file mode 100644
index 000000000000..36c157bfa9a2
--- /dev/null
+++ b/plugins/groovy/resources/intentionDescriptions/SimplifyTernaryOperatorIntention/after.groovy.template
@@ -0,0 +1,2 @@
+def x = a && b
+def y = c || d
diff --git a/plugins/groovy/resources/intentionDescriptions/SimplifyTernaryOperatorIntention/before.groovy.template b/plugins/groovy/resources/intentionDescriptions/SimplifyTernaryOperatorIntention/before.groovy.template
new file mode 100644
index 000000000000..bff2013b1b57
--- /dev/null
+++ b/plugins/groovy/resources/intentionDescriptions/SimplifyTernaryOperatorIntention/before.groovy.template
@@ -0,0 +1,2 @@
+def x = a ? b : false
+def y = c ? true : d
diff --git a/plugins/groovy/resources/intentionDescriptions/SimplifyTernaryOperatorIntention/description.html b/plugins/groovy/resources/intentionDescriptions/SimplifyTernaryOperatorIntention/description.html
new file mode 100644
index 000000000000..818797709d78
--- /dev/null
+++ b/plugins/groovy/resources/intentionDescriptions/SimplifyTernaryOperatorIntention/description.html
@@ -0,0 +1,25 @@
+
+
+
+
+This intention simplifies a conditional operator ?: when one of the branches is a constant true or false.
+
+ So a ? b : false gets turned into a && b and a ? true : b gets turned into a || b.
+ While you would normally not write such code, you may end up with it as a result of other refactorings.
+
+
+
diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml
index 7799ebd9c2c7..13f1dd3e41b5 100644
--- a/plugins/groovy/src/META-INF/plugin.xml
+++ b/plugins/groovy/src/META-INF/plugin.xml
@@ -31,14 +31,17 @@
-
+
-
+
-
+
@@ -46,10 +49,12 @@
-
+
-
+
@@ -59,7 +64,8 @@
-
+
@@ -67,7 +73,7 @@
+ implementation="org.jetbrains.plugins.groovy.annotator.DefaultGroovyFrameworkConfigNotification"/>
@@ -92,11 +98,15 @@
-
-
-
+
+
+
-
+
@@ -133,16 +143,23 @@
-
+
-
-
+
+
-
-
+
+
-
+
@@ -153,9 +170,11 @@
-
+
-
+
@@ -170,9 +189,12 @@
-
-
-
+
+
+
@@ -197,7 +219,8 @@
-
+
-
+
-
+
-
-
-
+
+
+
-
-
-
-
+
+
+
+
-
-
+
+
+ id="groovyReferenceListWeigher" order="before openedInEditor"/>
@@ -287,7 +321,7 @@
-
+
@@ -319,7 +353,8 @@
-
+
@@ -335,7 +370,7 @@
-
+
@@ -395,9 +430,9 @@
-
+
-
+
@@ -409,160 +444,212 @@
groupName="Declaration redundancy" enabledByDefault="true" level="WARNING"
implementationClass="org.jetbrains.plugins.groovy.codeInspection.GroovyUnusedDeclarationInspection"/>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -769,6 +909,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.SimplifyTernaryOperatorIntention
+
org.jetbrains.plugins.groovy.intentions.GroovyIntentionsBundle
intention.category.groovy/intention.category.control.flow
@@ -882,9 +1027,9 @@
org.jetbrains.plugins.groovy.intentions.conversions.ConvertIntegerToOctalIntention
- org.jetbrains.plugins.groovy.intentions.GroovyIntentionsBundle
- intention.category.groovy/intention.category.conversions
- org.jetbrains.plugins.groovy.intentions.conversions.ConvertIntegerToBinaryIntention
+ org.jetbrains.plugins.groovy.intentions.GroovyIntentionsBundle
+ intention.category.groovy/intention.category.conversions
+ org.jetbrains.plugins.groovy.intentions.conversions.ConvertIntegerToBinaryIntention
org.jetbrains.plugins.groovy.intentions.GroovyIntentionsBundle
@@ -1041,10 +1186,12 @@
serviceImplementation="org.jetbrains.plugins.groovy.mvc.MvcConsole"/>
+ serviceInterface="org.jetbrains.plugins.groovy.mvc.MvcRunTargetHistoryService"/>
-
-
+
+
@@ -1052,7 +1199,8 @@
-
+
@@ -1074,20 +1222,22 @@
-
-
+
+
-
+
+ internal="true"/>
+ internal="true"/>
+ text="Synchronize Griffon settings"
+ description="Refresh IntelliJ IDEA project structure so that it matches Griffon build settings">
aaa || bbb
+ GrExpression conditionExp = condExp.getCondition();
+
+ String conditionExpText = getStringToPutIntoOrExpression(conditionExp);
+ String elseExpText = getStringToPutIntoOrExpression(elseBranch);
+ String newExp = conditionExpText + "||" + elseExpText;
+ int caretOffset = conditionExpText.length() + 2; // after "||"
+
+ GrExpression expressionFromText = groovyPsiElementFactory.createExpressionFromText(newExp, condExp.getContext());
+
+ expressionFromText = (GrExpression)condExp.replace(expressionFromText);
+
+ editor.getCaretModel().moveToOffset(expressionFromText.getTextOffset() + caretOffset); // just past "||"
+ return;
+ }
+
+ Object elseVal = GroovyConstantExpressionEvaluator.evaluate(elseBranch);
+ if (Boolean.FALSE.equals(elseVal) && thenBranch != null) {
+ // aaa ? bbb : false -> aaa && bbb
+ GrExpression conditionExp = condExp.getCondition();
+
+ String conditionExpText = getStringToPutIntoAndExpression(conditionExp);
+ String thenExpText = getStringToPutIntoAndExpression(thenBranch);
+
+
+ String newExp = conditionExpText + "&&" + thenExpText;
+ int caretOffset = conditionExpText.length() + 2; // after "&&"
+ GrExpression expressionFromText = groovyPsiElementFactory.createExpressionFromText(newExp, condExp.getContext());
+
+ expressionFromText = (GrExpression)condExp.replace(expressionFromText);
+
+ editor.getCaretModel().moveToOffset(expressionFromText.getTextOffset() + caretOffset); // just past "&&"
+ }
+
+ }
+
+ /**
+ * Convert an expression into something which can be put inside ( a && b )
+ * Wrap in parenthesis, if necessary
+ *
+ * @param expression
+ * @return a string representing the expression
+ */
+ @NotNull
+ private static String getStringToPutIntoAndExpression(GrExpression expression) {
+ String expressionText = expression.getText();
+ if (ParenthesesUtils.AND_PRECEDENCE < ParenthesesUtils.getPrecendence(expression)) {
+ expressionText = "(" + expressionText + ")";
+ }
+ return expressionText;
+ }
+
+ @NotNull
+ private static String getStringToPutIntoOrExpression(GrExpression expression) {
+ String expressionText = expression.getText();
+ if (ParenthesesUtils.OR_PRECEDENCE < ParenthesesUtils.getPrecendence(expression)) {
+ expressionText = "(" + expressionText + ")";
+ }
+ return expressionText;
+ }
+
+ @NotNull
+ @Override
+ protected PsiElementPredicate getElementPredicate() {
+ return new PsiElementPredicate() {
+ @Override
+ public boolean satisfiedBy(PsiElement element) {
+ if (!(element instanceof GrConditionalExpression)) {
+ return false;
+ }
+
+ GrConditionalExpression condExp = (GrConditionalExpression)element;
+ GrExpression thenBranch = condExp.getThenBranch();
+ GrExpression elseBranch = condExp.getElseBranch();
+
+ Object thenVal = GroovyConstantExpressionEvaluator.evaluate(thenBranch);
+ if (Boolean.TRUE.equals(thenVal) && elseBranch != null) {
+ return true;
+ }
+
+ Object elseVal = GroovyConstantExpressionEvaluator.evaluate(elseBranch);
+ if (thenBranch != null && Boolean.FALSE.equals(elseVal)) {
+ return true;
+ }
+
+ return false;
+ }
+ };
+ }
+}
diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/intentions/SimplifyTernaryOperatorTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/intentions/SimplifyTernaryOperatorTest.groovy
new file mode 100644
index 000000000000..c288f57c3146
--- /dev/null
+++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/intentions/SimplifyTernaryOperatorTest.groovy
@@ -0,0 +1,63 @@
+/*
+ * 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.intentions
+
+/**
+ * @author Niels Harremoes
+ * @author Oscar Toernroth
+ */
+class SimplifyTernaryOperatorTest extends GrIntentionTestCase {
+
+ String intentionName = GroovyIntentionsBundle.message("simplify.ternary.operator.intention.name")
+
+ private void doTest(String before, String after) {
+
+ doTextTest before, intentionName, after
+ }
+
+ public void testDoNotTriggerOnNormalConditional() throws Exception {
+ doAntiTest 'aaa ? bbb : ccc', intentionName
+ doAntiTest 'aaa ? false : ccc', intentionName
+ doAntiTest 'aaa ? bbb : true', intentionName
+
+ }
+
+
+ public void testSimplifyWhenThenIsTrue() throws Exception {
+ doTest 'aaa ? true: bbb', 'aaa || bbb'
+ }
+
+
+ public void testSimplifyWhenThenIsTrueComplexArguments() throws Exception {
+ doTest 'aaa ? true : bbb ? ccc : ddd', 'aaa || (bbb ? ccc : ddd)'
+ }
+
+ public void testSimplifyWhenElseIsFalseComplexArguments() throws Exception {
+ doTest 'aaa || bbb ? ccc || ddd : false', '(aaa || bbb) && (ccc || ddd)'
+ }
+
+ public void testSimplifyWhenElseIsFalse() throws Exception {
+ doTest 'aaa ? bbb : false', 'aaa && bbb'
+ }
+
+ // aaa ? false : bbb -> (!aaa) && bbb
+
+ public void testResultisWrappedInParenthesesWhenNeeded() throws Exception {
+ doTest 'a ? b ? true : d : false', 'a && (b ? true : d)'
+ }
+
+
+}