diff --git a/idea/src/org/jetbrains/jet/plugin/intentions/SplitIfIntention.kt b/idea/src/org/jetbrains/jet/plugin/intentions/SplitIfIntention.kt index 23f977e8f12..dec112ee235 100644 --- a/idea/src/org/jetbrains/jet/plugin/intentions/SplitIfIntention.kt +++ b/idea/src/org/jetbrains/jet/plugin/intentions/SplitIfIntention.kt @@ -22,13 +22,76 @@ import org.jetbrains.jet.lang.psi.JetBinaryExpression import org.jetbrains.jet.lang.psi.JetPsiFactory import org.jetbrains.jet.lexer.JetTokens import org.jetbrains.jet.lang.psi.psiUtil.getParentByType -import org.jetbrains.jet.lang.psi.JetExpression import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.jet.lang.psi.JetSimpleNameExpression +import org.jetbrains.jet.lang.psi.JetExpression +import org.jetbrains.jet.lang.psi.JetPsiUtil -public class SplitIfIntention : JetSelfTargetingIntention("split.if", javaClass()) { +public class SplitIfIntention : JetSelfTargetingIntention("split.if", javaClass()) { + override fun isApplicableTo(element: JetExpression): Boolean { + throw IllegalStateException("isApplicableTo(JetExpression, Editor) should be called instead") + } - override fun isApplicableTo(element: JetSimpleNameExpression): Boolean { + override fun isApplicableTo(element: JetExpression, editor: Editor): Boolean { + if (element !is JetSimpleNameExpression && element !is JetIfExpression) return false + + if (element is JetSimpleNameExpression) { + return isOperatorValid(element) + } + + if (element is JetIfExpression) { + if (!isCursorOnIfKeyword(element, editor)) return false + if (getFirstValidOperator(element) == null) return false + } + return true + } + + override fun applyTo(element: JetExpression, editor: Editor) { + val currentElement = when (element) { + is JetIfExpression -> getFirstValidOperator(element) + else -> element as JetSimpleNameExpression + } + + val ifExpression = currentElement!!.getParentByType(javaClass()) + val expression = currentElement.getParent() as JetBinaryExpression + val rightExpression = getRight(expression, ifExpression!!.getCondition() as JetExpression) + val leftExpression = expression.getLeft() + val elseExpression = ifExpression.getElse() + val thenExpression = ifExpression.getThen() + + if (currentElement.getReferencedNameElementType() == JetTokens.ANDAND) { + ifExpression.replace(JetPsiFactory.createIf(element.getProject(), leftExpression, + JetPsiFactory.wrapInABlock(JetPsiFactory.createIf(element.getProject(), rightExpression, thenExpression, + elseExpression)), elseExpression)) + } + else { + ifExpression.replace(JetPsiFactory.createIf(element.getProject(), leftExpression, thenExpression, + JetPsiFactory.createIf(element.getProject(), rightExpression, thenExpression, elseExpression))) + } + } + + fun getRight(element: JetBinaryExpression, condition: JetExpression): JetExpression { + //gets the textOffset of the right side of the JetBinaryExpression in context to condition + val startOffset = element.getRight()!!.getTextOffset() - condition.getTextOffset() + val rightString = condition.getText()!![startOffset, condition.getTextLength()].toString() + + return JetPsiFactory.createExpression(element.getProject(), rightString) + } + + fun isCursorOnIfKeyword(element: JetIfExpression, editor: Editor): Boolean { + val ifKeyword = JetPsiUtil.findChildByType(element, JetTokens.IF_KEYWORD) ?: return false + val cursor = editor.getCaretModel().getOffset() + return (cursor >= ifKeyword.getTextOffset() && cursor <= ifKeyword.getTextOffset() + ifKeyword.getTextLength()) + } + + fun getFirstValidOperator(element: JetIfExpression): JetSimpleNameExpression? { + if (element.getCondition() == null) return null + val condition = element.getCondition() + val childElements = PsiTreeUtil.findChildrenOfType(condition, javaClass()) + return childElements.firstOrNull { isOperatorValid(it) } + } + + fun isOperatorValid(element: JetSimpleNameExpression): Boolean { val operator = element.getReferencedNameElementType() if (operator != JetTokens.ANDAND && operator != JetTokens.OROR) return false @@ -53,32 +116,5 @@ public class SplitIfIntention : JetSelfTargetingIntention()) - val expression = element.getParent() as JetBinaryExpression - val rightExpression = getRight(expression, ifExpression!!.getCondition() as JetExpression) - val leftExpression = expression.getLeft() - val elseExpression = ifExpression.getElse() - val thenExpression = ifExpression.getThen() - - if (element.getReferencedNameElementType() == JetTokens.ANDAND) { - ifExpression.replace(JetPsiFactory.createIf(element.getProject(), leftExpression, - JetPsiFactory.wrapInABlock(JetPsiFactory.createIf(element.getProject(), rightExpression, thenExpression, - elseExpression)), elseExpression)) - } - else { - ifExpression.replace(JetPsiFactory.createIf(element.getProject(), leftExpression, thenExpression, - JetPsiFactory.createIf(element.getProject(), rightExpression, thenExpression, elseExpression))) - } - } - - fun getRight(element: JetBinaryExpression, condition: JetExpression): JetExpression { - //gets the textOffset of the right side of the JetBinaryExpression in context to condition - val startOffset = element.getRight()!!.getTextOffset() - condition.getTextOffset() - val rightString = condition.getText()!![startOffset, condition.getTextLength()].toString() - - return JetPsiFactory.createExpression(element.getProject(), rightString) - } } diff --git a/idea/testData/intentions/splitIf/splitIfAndOrWithBraces.kt b/idea/testData/intentions/splitIf/splitIfAndOrWithBraces.kt new file mode 100644 index 00000000000..44c1b6e93f5 --- /dev/null +++ b/idea/testData/intentions/splitIf/splitIfAndOrWithBraces.kt @@ -0,0 +1,11 @@ +fun doSomething(a: T) {} + +fun foo() { + val a = true + val b = false + val c = true + val d = false + if ((a && b) && c || d) { + doSomething("test") + } +} diff --git a/idea/testData/intentions/splitIf/splitIfAndOrWithBraces.kt.after b/idea/testData/intentions/splitIf/splitIfAndOrWithBraces.kt.after new file mode 100644 index 00000000000..df9fd318d5d --- /dev/null +++ b/idea/testData/intentions/splitIf/splitIfAndOrWithBraces.kt.after @@ -0,0 +1,13 @@ +fun doSomething(a: T) {} + +fun foo() { + val a = true + val b = false + val c = true + val d = false + if ((a && b) && c) { + doSomething("test") + } else if (d) { + doSomething("test") + } +} diff --git a/idea/testData/intentions/splitIf/splitIfAndWithBraces.kt b/idea/testData/intentions/splitIf/splitIfAndWithBraces.kt new file mode 100644 index 00000000000..97c2d46967e --- /dev/null +++ b/idea/testData/intentions/splitIf/splitIfAndWithBraces.kt @@ -0,0 +1,11 @@ +fun doSomething(a: T) {} + +fun foo() { + val a = true + val b = false + val c = true + val d = false + if ((a || b) && (c || d)) { + doSomething("test") + } +} diff --git a/idea/testData/intentions/splitIf/splitIfAndWithBraces.kt.after b/idea/testData/intentions/splitIf/splitIfAndWithBraces.kt.after new file mode 100644 index 00000000000..85257391c65 --- /dev/null +++ b/idea/testData/intentions/splitIf/splitIfAndWithBraces.kt.after @@ -0,0 +1,13 @@ +fun doSomething(a: T) {} + +fun foo() { + val a = true + val b = false + val c = true + val d = false + if (a || b) { + if (c || d) { + doSomething("test") + } + } +} diff --git a/idea/testData/intentions/splitIf/splitIfCaretOnIf.kt b/idea/testData/intentions/splitIf/splitIfCaretOnIf.kt new file mode 100644 index 00000000000..0a65d3bf7d7 --- /dev/null +++ b/idea/testData/intentions/splitIf/splitIfCaretOnIf.kt @@ -0,0 +1,9 @@ +fun doSomething(a: T) {} + +fun foo() { + val a = true + val b = false + if (a && b) { + doSomething("test") + } +} \ No newline at end of file diff --git a/idea/testData/intentions/splitIf/splitIfCaretOnIf.kt.after b/idea/testData/intentions/splitIf/splitIfCaretOnIf.kt.after new file mode 100644 index 00000000000..fd252316578 --- /dev/null +++ b/idea/testData/intentions/splitIf/splitIfCaretOnIf.kt.after @@ -0,0 +1,11 @@ +fun doSomething(a: T) {} + +fun foo() { + val a = true + val b = false + if (a) { + if (b) { + doSomething("test") + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/splitIf/splitIfNestedIf.kt b/idea/testData/intentions/splitIf/splitIfNestedIf.kt new file mode 100644 index 00000000000..c2d296ea0d8 --- /dev/null +++ b/idea/testData/intentions/splitIf/splitIfNestedIf.kt @@ -0,0 +1,12 @@ +fun doSomething(a: T) {} + +fun foo() { + val a = true + val b = false + val c = true + if (a) { + if (b && c) { + doSomething("test") + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/splitIf/splitIfNestedIf.kt.after b/idea/testData/intentions/splitIf/splitIfNestedIf.kt.after new file mode 100644 index 00000000000..ef1618e5874 --- /dev/null +++ b/idea/testData/intentions/splitIf/splitIfNestedIf.kt.after @@ -0,0 +1,14 @@ +fun doSomething(a: T) {} + +fun foo() { + val a = true + val b = false + val c = true + if (a) { + if (b) { + if (c) { + doSomething("test") + } + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/splitIf/splitIfNestedIfNotApplicable.kt b/idea/testData/intentions/splitIf/splitIfNestedIfNotApplicable.kt new file mode 100644 index 00000000000..ba68cd50cf5 --- /dev/null +++ b/idea/testData/intentions/splitIf/splitIfNestedIfNotApplicable.kt @@ -0,0 +1,13 @@ +// IS_APPLICABLE: false +fun doSomething(a: T) {} + +fun foo() { + val a = true + val b = false + val c = true + if (a) { + if (b && c) { + doSomething("test") + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/splitIf/splitIfNestedInside.kt b/idea/testData/intentions/splitIf/splitIfNestedInside.kt new file mode 100644 index 00000000000..b2bcc2361a8 --- /dev/null +++ b/idea/testData/intentions/splitIf/splitIfNestedInside.kt @@ -0,0 +1,13 @@ +fun doSomething(a: T) {} + +fun foo() { + val a = true + val b = false + val c = true + val d = false + if (a || b) { + if (c || d) { + doSomething("test") + } + } +} diff --git a/idea/testData/intentions/splitIf/splitIfNestedInside.kt.after b/idea/testData/intentions/splitIf/splitIfNestedInside.kt.after new file mode 100644 index 00000000000..4d8ee82c746 --- /dev/null +++ b/idea/testData/intentions/splitIf/splitIfNestedInside.kt.after @@ -0,0 +1,15 @@ +fun doSomething(a: T) {} + +fun foo() { + val a = true + val b = false + val c = true + val d = false + if (a || b) { + if (c) { + doSomething("test") + } else if (d) { + doSomething("test") + } + } +} diff --git a/idea/testData/intentions/splitIf/splitIfNestedOutside.kt b/idea/testData/intentions/splitIf/splitIfNestedOutside.kt new file mode 100644 index 00000000000..487857e33da --- /dev/null +++ b/idea/testData/intentions/splitIf/splitIfNestedOutside.kt @@ -0,0 +1,13 @@ +fun doSomething(a: T) {} + +fun foo() { + val a = true + val b = false + val c = true + val d = false + if (a && b) { + if (c && d) { + doSomething("test") + } + } +} diff --git a/idea/testData/intentions/splitIf/splitIfNestedOutside.kt.after b/idea/testData/intentions/splitIf/splitIfNestedOutside.kt.after new file mode 100644 index 00000000000..a5301be50ca --- /dev/null +++ b/idea/testData/intentions/splitIf/splitIfNestedOutside.kt.after @@ -0,0 +1,15 @@ +fun doSomething(a: T) {} + +fun foo() { + val a = true + val b = false + val c = true + val d = false + if (a) { + if (b) { + if (c && d) { + doSomething("test") + } + } + } +} diff --git a/idea/testData/intentions/splitIf/splitIfOnIfWithOr.kt b/idea/testData/intentions/splitIf/splitIfOnIfWithOr.kt new file mode 100644 index 00000000000..3c9a241f2a9 --- /dev/null +++ b/idea/testData/intentions/splitIf/splitIfOnIfWithOr.kt @@ -0,0 +1,10 @@ +fun doSomething(a: T) {} + +fun foo() { + val a = true + val b = false + val c = true + if (a && b || c) { + doSomething("test") + } +} \ No newline at end of file diff --git a/idea/testData/intentions/splitIf/splitIfOnIfWithOr.kt.after b/idea/testData/intentions/splitIf/splitIfOnIfWithOr.kt.after new file mode 100644 index 00000000000..77bc8da7498 --- /dev/null +++ b/idea/testData/intentions/splitIf/splitIfOnIfWithOr.kt.after @@ -0,0 +1,12 @@ +fun doSomething(a: T) {} + +fun foo() { + val a = true + val b = false + val c = true + if (a && b) { + doSomething("test") + } else if (c) { + doSomething("test") + } +} \ No newline at end of file diff --git a/idea/testData/intentions/splitIf/splitIfOrWithBraces.kt b/idea/testData/intentions/splitIf/splitIfOrWithBraces.kt new file mode 100644 index 00000000000..dc7b5168b83 --- /dev/null +++ b/idea/testData/intentions/splitIf/splitIfOrWithBraces.kt @@ -0,0 +1,11 @@ +fun doSomething(a: T) {} + +fun foo() { + val a = true + val b = false + val c = true + val d = false + if ((a && b || c) || d) { + doSomething("test") + } +} diff --git a/idea/testData/intentions/splitIf/splitIfOrWithBraces.kt.after b/idea/testData/intentions/splitIf/splitIfOrWithBraces.kt.after new file mode 100644 index 00000000000..6519bba8ef6 --- /dev/null +++ b/idea/testData/intentions/splitIf/splitIfOrWithBraces.kt.after @@ -0,0 +1,13 @@ +fun doSomething(a: T) {} + +fun foo() { + val a = true + val b = false + val c = true + val d = false + if (a && b || c) { + doSomething("test") + } else if (d) { + doSomething("test") + } +} diff --git a/idea/tests/org/jetbrains/jet/plugin/intentions/CodeTransformationTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/intentions/CodeTransformationTestGenerated.java index c6eeda86bcb..26eb1d79fef 100644 --- a/idea/tests/org/jetbrains/jet/plugin/intentions/CodeTransformationTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/intentions/CodeTransformationTestGenerated.java @@ -3026,11 +3026,51 @@ public class CodeTransformationTestGenerated extends AbstractCodeTransformationT doTestSplitIf("idea/testData/intentions/splitIf/splitIfAndOr.kt"); } + @TestMetadata("splitIfAndOrWithBraces.kt") + public void testSplitIfAndOrWithBraces() throws Exception { + doTestSplitIf("idea/testData/intentions/splitIf/splitIfAndOrWithBraces.kt"); + } + + @TestMetadata("splitIfAndWithBraces.kt") + public void testSplitIfAndWithBraces() throws Exception { + doTestSplitIf("idea/testData/intentions/splitIf/splitIfAndWithBraces.kt"); + } + + @TestMetadata("splitIfCaretOnIf.kt") + public void testSplitIfCaretOnIf() throws Exception { + doTestSplitIf("idea/testData/intentions/splitIf/splitIfCaretOnIf.kt"); + } + + @TestMetadata("splitIfNestedIf.kt") + public void testSplitIfNestedIf() throws Exception { + doTestSplitIf("idea/testData/intentions/splitIf/splitIfNestedIf.kt"); + } + + @TestMetadata("splitIfNestedIfNotApplicable.kt") + public void testSplitIfNestedIfNotApplicable() throws Exception { + doTestSplitIf("idea/testData/intentions/splitIf/splitIfNestedIfNotApplicable.kt"); + } + + @TestMetadata("splitIfNestedInside.kt") + public void testSplitIfNestedInside() throws Exception { + doTestSplitIf("idea/testData/intentions/splitIf/splitIfNestedInside.kt"); + } + + @TestMetadata("splitIfNestedOutside.kt") + public void testSplitIfNestedOutside() throws Exception { + doTestSplitIf("idea/testData/intentions/splitIf/splitIfNestedOutside.kt"); + } + @TestMetadata("splitIfNotIf.kt") public void testSplitIfNotIf() throws Exception { doTestSplitIf("idea/testData/intentions/splitIf/splitIfNotIf.kt"); } + @TestMetadata("splitIfOnIfWithOr.kt") + public void testSplitIfOnIfWithOr() throws Exception { + doTestSplitIf("idea/testData/intentions/splitIf/splitIfOnIfWithOr.kt"); + } + @TestMetadata("splitIfOneAND.kt") public void testSplitIfOneAND() throws Exception { doTestSplitIf("idea/testData/intentions/splitIf/splitIfOneAND.kt"); @@ -3051,6 +3091,11 @@ public class CodeTransformationTestGenerated extends AbstractCodeTransformationT doTestSplitIf("idea/testData/intentions/splitIf/splitIfOrAnd.kt"); } + @TestMetadata("splitIfOrWithBraces.kt") + public void testSplitIfOrWithBraces() throws Exception { + doTestSplitIf("idea/testData/intentions/splitIf/splitIfOrWithBraces.kt"); + } + @TestMetadata("splitIfTwoOperatorsFirst.kt") public void testSplitIfTwoOperatorsFirst() throws Exception { doTestSplitIf("idea/testData/intentions/splitIf/splitIfTwoOperatorsFirst.kt");