diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt index aa36b44daef..2ca36229e5c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt @@ -349,8 +349,11 @@ public class JetPsiFactory(private val project: Project) { return createReturn(JetPsiUtil.getText(expression)) } - public fun createIf(condition: JetExpression?, thenExpr: JetExpression?, elseExpr: JetExpression?): JetIfExpression { - return createExpression(JetPsiUnparsingUtils.toIf(condition, thenExpr, elseExpr)) as JetIfExpression + public fun createIf(condition: JetExpression, thenExpr: JetExpression, elseExpr: JetExpression?): JetIfExpression { + return (if (elseExpr != null) + createExpressionByPattern("if ($0) $1 else $2", condition, thenExpr, elseExpr) as JetIfExpression + else + createExpressionByPattern("if ($0) $1", condition, thenExpr)) as JetIfExpression } public fun createArgumentWithName(name: String?, argumentExpression: JetExpression): JetValueArgument { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiUnparsingUtils.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiUnparsingUtils.java index dc8868e93dd..3cf86d6ca92 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiUnparsingUtils.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiUnparsingUtils.java @@ -23,20 +23,6 @@ public class JetPsiUnparsingUtils { private JetPsiUnparsingUtils() { } - @NotNull - public static String toIf(@Nullable JetExpression condition, @Nullable JetExpression thenExpression, @Nullable JetExpression elseExpression) { - return toIf( - JetPsiUtil.getText(condition), - JetPsiUtil.getText(thenExpression), - elseExpression != null ? elseExpression.getText() : null - ); - } - - @NotNull - public static String toIf(@NotNull String condition, @NotNull String thenExpression, @Nullable String elseExpression) { - return "if " + parenthesizeTextIfNeeded(condition) + " " + thenExpression + (elseExpression != null ? " else " + elseExpression : ""); - } - @NotNull public static String toBinaryExpression(@Nullable JetExpression left, @NotNull String op, @Nullable JetElement right) { return toBinaryExpression(JetPsiUtil.getText(left), op, JetPsiUtil.getText(right)); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt index dc8cc27dd7b..90fd4b7d459 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt @@ -80,9 +80,6 @@ public fun JetPsiFactory.createExpressionByPattern(pattern: String, vararg args: bound = range.getStartOffset() + start } expression = codeStyleManager.reformatRange(expression, start, bound + 1, true) as JetExpression - - // we need to adjust indent of all lines within expression - codeStyleManager.adjustLineIndent(expression.getContainingFile(), expression.getTextRange()) } // do not reformat the whole expression in PostprocessReformattingAspect @@ -97,6 +94,8 @@ public fun JetPsiFactory.createExpressionByPattern(pattern: String, vararg args: element.replace(arg) } + codeStyleManager.adjustLineIndent(expression.getContainingFile(), expression.getTextRange()) + return expression } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt index b9b421a22a2..c322f1011bc 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt @@ -46,8 +46,7 @@ import org.jetbrains.kotlin.JetNodeTypes import org.jetbrains.kotlin.name.FqName public fun JetCallElement.getCallNameExpression(): JetSimpleNameExpression? { - val calleeExpression = getCalleeExpression() - if (calleeExpression == null) return null + val calleeExpression = getCalleeExpression() ?: return null return when (calleeExpression) { is JetSimpleNameExpression -> calleeExpression diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties index 23e8bf54c69..c04615126b1 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties @@ -288,8 +288,6 @@ add.name.to.argument.single=Add name to argument\: ''{0}'' add.name.to.argument.multiple=Add name to argument... add.name.to.argument.action=Add name to argument... add.name.to.parameter.name.chooser.title=Choose parameter name -split.if=Split into 2 if's -split.if.family=Split If simplify.boolean.with.constants=Simplify boolean expression simplify.boolean.with.constants.family=Simplify Boolean Expression remove.explicit.type.arguments=Remove explicit type arguments diff --git a/idea/src/org/jetbrains/kotlin/idea/codeInsight/unwrap/KotlinUnwrappers.java b/idea/src/org/jetbrains/kotlin/idea/codeInsight/unwrap/KotlinUnwrappers.java index f9cbb900497..b99832ab1a1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/codeInsight/unwrap/KotlinUnwrappers.java +++ b/idea/src/org/jetbrains/kotlin/idea/codeInsight/unwrap/KotlinUnwrappers.java @@ -68,7 +68,10 @@ public class KotlinUnwrappers { @Override public boolean isApplicableTo(PsiElement e) { - return e instanceof JetIfExpression && ((JetIfExpression) e).getElse() != null; + return e instanceof JetIfExpression + && ((JetIfExpression) e).getCondition() != null + && ((JetIfExpression) e).getThen() != null + && ((JetIfExpression) e).getElse() != null; } @Override diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/SplitIfIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/SplitIfIntention.kt index 0a990e8aa8e..ca774aa17cd 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/SplitIfIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/SplitIfIntention.kt @@ -16,98 +16,74 @@ package org.jetbrains.kotlin.idea.intentions -import org.jetbrains.kotlin.psi.JetIfExpression import com.intellij.openapi.editor.Editor -import org.jetbrains.kotlin.psi.JetBinaryExpression -import org.jetbrains.kotlin.psi.JetPsiFactory import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.kotlin.psi.JetSimpleNameExpression -import org.jetbrains.kotlin.psi.JetExpression -import org.jetbrains.kotlin.psi.JetPsiUtil +import org.jetbrains.kotlin.psi.* -public class SplitIfIntention : JetSelfTargetingIntention("split.if", javaClass()) { +public class SplitIfIntention : JetSelfTargetingIntention(javaClass(), "Split if into 2 if's") { override fun isApplicableTo(element: JetExpression, caretOffset: Int): Boolean { - if (element !is JetSimpleNameExpression && element !is JetIfExpression) return false - - if (element is JetSimpleNameExpression) { - return isOperatorValid(element) + return when (element) { + is JetSimpleNameExpression -> isOperatorValid(element) + is JetIfExpression -> getFirstValidOperator(element) != null && element.getIfKeyword().getTextRange().containsOffset(caretOffset) + else -> false } - - if (element is JetIfExpression) { - if (!isCursorOnIfKeyword(element, caretOffset)) 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) + val operator = when (element) { + is JetIfExpression -> getFirstValidOperator(element)!! else -> element as JetSimpleNameExpression } - val ifExpression = currentElement!!.getNonStrictParentOfType() - val expression = currentElement.getParent() as JetBinaryExpression - val rightExpression = getRight(expression, ifExpression!!.getCondition()) - val leftExpression = expression.getLeft() + val ifExpression = operator.getNonStrictParentOfType() + val expression = operator.getParent() as JetBinaryExpression + val rightExpression = JetPsiUtil.safeDeparenthesize(getRight(expression, ifExpression!!.getCondition()!!)) + val leftExpression = JetPsiUtil.safeDeparenthesize(expression.getLeft()!!) + val thenExpression = ifExpression.getThen()!! val elseExpression = ifExpression.getElse() - val thenExpression = ifExpression.getThen() val psiFactory = JetPsiFactory(element) - if (currentElement.getReferencedNameElementType() == JetTokens.ANDAND) { - ifExpression.replace( - psiFactory.createIf(leftExpression, psiFactory.wrapInABlock( - psiFactory.createIf(rightExpression, thenExpression, elseExpression) - ), - elseExpression) - ) - } - else { - ifExpression.replace(psiFactory.createIf(leftExpression, thenExpression, - psiFactory.createIf(rightExpression, thenExpression, elseExpression)) - ) + val innerIf = psiFactory.createIf(rightExpression, thenExpression, elseExpression) + val newIf = when (operator.getReferencedNameElementType()) { + JetTokens.ANDAND -> psiFactory.createIf(leftExpression, psiFactory.wrapInABlock(innerIf), elseExpression) + + JetTokens.OROR -> psiFactory.createIf(leftExpression, thenExpression, innerIf) + + else -> throw IllegalArgumentException() } + ifExpression.replace(newIf) } private 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() + val rightString = condition.getText()!!.substring(startOffset, condition.getTextLength()) return JetPsiFactory(element).createExpression(rightString) } - private fun isCursorOnIfKeyword(element: JetIfExpression, offset: Int): Boolean { - val ifKeyword = JetPsiUtil.findChildByType(element, JetTokens.IF_KEYWORD) ?: return false - return (offset >= ifKeyword.getTextOffset() && offset <= ifKeyword.getTextOffset() + ifKeyword.getTextLength()) - } - private 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) } + val condition = element.getCondition() ?: return null + return PsiTreeUtil.findChildrenOfType(condition, javaClass()) + .firstOrNull { isOperatorValid(it) } } private fun isOperatorValid(element: JetSimpleNameExpression): Boolean { val operator = element.getReferencedNameElementType() if (operator != JetTokens.ANDAND && operator != JetTokens.OROR) return false - if (element.getParent() !is JetBinaryExpression) return false - var expression = element.getParent() as JetBinaryExpression + var expression = element.getParent() as? JetBinaryExpression ?: return false if (expression.getRight() == null || expression.getLeft() == null) return false - while (expression.getParent() is JetBinaryExpression) { - expression = expression.getParent() as JetBinaryExpression - if (operator == JetTokens.ANDAND && expression.getOperationToken() != JetTokens.ANDAND) return false - if (operator == JetTokens.OROR && expression.getOperationToken() != JetTokens.OROR) return false + while (true) { + expression = expression.getParent() as? JetBinaryExpression ?: break + if (expression.getOperationToken() != operator) return false } - if (expression.getParent()?.getParent() !is JetIfExpression) return false - val ifExpression = expression.getParent()?.getParent() as JetIfExpression + val ifExpression = expression.getParent()?.getParent() as? JetIfExpression ?: return false if (ifExpression.getCondition() == null) return false if (!PsiTreeUtil.isAncestor(ifExpression.getCondition(), element, false)) return false diff --git a/idea/testData/quickfix/createFromUsage/createClass/typeReference/annotationNotQualifierWithTypeArgs.kt b/idea/testData/quickfix/createFromUsage/createClass/typeReference/annotationNotQualifierWithTypeArgs.kt index 5d7a4f03ab0..f641b272254 100644 --- a/idea/testData/quickfix/createFromUsage/createClass/typeReference/annotationNotQualifierWithTypeArgs.kt +++ b/idea/testData/quickfix/createFromUsage/createClass/typeReference/annotationNotQualifierWithTypeArgs.kt @@ -2,6 +2,7 @@ // ACTION: Create class 'A' // ACTION: Create interface 'A' // ACTION: Convert to block body +// ACTION: Remove explicit type specification // ERROR: Unresolved reference: A package p diff --git a/idea/testData/quickfix/createFromUsage/createClass/typeReference/annotationQualifierNoTypeArgs.kt b/idea/testData/quickfix/createFromUsage/createClass/typeReference/annotationQualifierNoTypeArgs.kt index 302cb1d0532..91cf14d2b87 100644 --- a/idea/testData/quickfix/createFromUsage/createClass/typeReference/annotationQualifierNoTypeArgs.kt +++ b/idea/testData/quickfix/createFromUsage/createClass/typeReference/annotationQualifierNoTypeArgs.kt @@ -4,6 +4,7 @@ // ACTION: Create interface 'A' // ACTION: Create enum 'A' // ACTION: Convert to block body +// ACTION: Remove explicit type specification // ERROR: Unresolved reference: A package p diff --git a/idea/testData/quickfix/createFromUsage/createClass/typeReference/classLibTypeReceiver.kt b/idea/testData/quickfix/createFromUsage/createClass/typeReference/classLibTypeReceiver.kt index 86bdc9299df..faa407f9843 100644 --- a/idea/testData/quickfix/createFromUsage/createClass/typeReference/classLibTypeReceiver.kt +++ b/idea/testData/quickfix/createFromUsage/createClass/typeReference/classLibTypeReceiver.kt @@ -1,5 +1,6 @@ // "Create class 'A'" "false" // ACTION: Convert to block body +// ACTION: Remove explicit type specification // ERROR: Unresolved reference: A package p diff --git a/idea/testData/quickfix/createFromUsage/createClass/typeReference/enumEntryNotQualifierNoTypeArgs.kt b/idea/testData/quickfix/createFromUsage/createClass/typeReference/enumEntryNotQualifierNoTypeArgs.kt index 2c513465470..a0a4510e66e 100644 --- a/idea/testData/quickfix/createFromUsage/createClass/typeReference/enumEntryNotQualifierNoTypeArgs.kt +++ b/idea/testData/quickfix/createFromUsage/createClass/typeReference/enumEntryNotQualifierNoTypeArgs.kt @@ -4,6 +4,7 @@ // ACTION: Create interface 'A' // ACTION: Create enum 'A' // ACTION: Convert to block body +// ACTION: Remove explicit type specification // ERROR: Unresolved reference: A package p diff --git a/idea/testData/quickfix/createFromUsage/createClass/typeReference/enumNotQualifierWithTypeArgs.kt b/idea/testData/quickfix/createFromUsage/createClass/typeReference/enumNotQualifierWithTypeArgs.kt index d1884aba818..b58910ffa47 100644 --- a/idea/testData/quickfix/createFromUsage/createClass/typeReference/enumNotQualifierWithTypeArgs.kt +++ b/idea/testData/quickfix/createFromUsage/createClass/typeReference/enumNotQualifierWithTypeArgs.kt @@ -2,6 +2,7 @@ // ACTION: Create class 'A' // ACTION: Create interface 'A' // ACTION: Convert to block body +// ACTION: Remove explicit type specification // ERROR: Unresolved reference: A package p diff --git a/idea/testData/quickfix/createFromUsage/createClass/typeReference/objectNotQualifierNoTypeArgs.kt b/idea/testData/quickfix/createFromUsage/createClass/typeReference/objectNotQualifierNoTypeArgs.kt index d25e6bb96f3..01a2d1ee6b7 100644 --- a/idea/testData/quickfix/createFromUsage/createClass/typeReference/objectNotQualifierNoTypeArgs.kt +++ b/idea/testData/quickfix/createFromUsage/createClass/typeReference/objectNotQualifierNoTypeArgs.kt @@ -4,6 +4,7 @@ // ACTION: Create interface 'A' // ACTION: Create enum 'A' // ACTION: Convert to block body +// ACTION: Remove explicit type specification // ERROR: Unresolved reference: A package p diff --git a/idea/testData/quickfix/createFromUsage/createClass/typeReference/objectNotQualifierWithTypeArgs.kt b/idea/testData/quickfix/createFromUsage/createClass/typeReference/objectNotQualifierWithTypeArgs.kt index 8f9ce8bb3e9..9c9a52d7544 100644 --- a/idea/testData/quickfix/createFromUsage/createClass/typeReference/objectNotQualifierWithTypeArgs.kt +++ b/idea/testData/quickfix/createFromUsage/createClass/typeReference/objectNotQualifierWithTypeArgs.kt @@ -2,6 +2,7 @@ // ACTION: Create class 'A' // ACTION: Create interface 'A' // ACTION: Convert to block body +// ACTION: Remove explicit type specification // ERROR: Unresolved reference: A package p