Refactored SimplifyBooleanWithConstantsIntention

This commit is contained in:
Valentin Kipyatkov
2015-05-05 17:07:02 +03:00
parent 1fa14af02f
commit 0ca492464b
3 changed files with 66 additions and 74 deletions
@@ -67,6 +67,7 @@ public class JetBinaryExpression extends JetExpressionImpl implements JetOperati
return (JetSimpleNameExpression) findChildByType(JetNodeTypes.OPERATION_REFERENCE); return (JetSimpleNameExpression) findChildByType(JetNodeTypes.OPERATION_REFERENCE);
} }
@NotNull
public IElementType getOperationToken() { public IElementType getOperationToken() {
return getOperationReference().getReferencedNameElementType(); return getOperationReference().getReferencedNameElementType();
} }
@@ -284,8 +284,6 @@ add.name.to.argument.single=Add name to argument\: ''{0}''
add.name.to.argument.multiple=Add name to argument... add.name.to.argument.multiple=Add name to argument...
add.name.to.argument.action=Add name to argument... add.name.to.argument.action=Add name to argument...
add.name.to.parameter.name.chooser.title=Choose parameter name add.name.to.parameter.name.chooser.title=Choose parameter name
simplify.boolean.with.constants=Simplify boolean expression
simplify.boolean.with.constants.family=Simplify Boolean Expression
remove.explicit.type.arguments=Remove explicit type arguments remove.explicit.type.arguments=Remove explicit type arguments
remove.explicit.type.arguments.family=Remove Explicit Type Arguments remove.explicit.type.arguments.family=Remove Explicit Type Arguments
convert.if.with.throw.to.assert=Replace 'if' with 'assert' statement convert.if.with.throw.to.assert=Replace 'if' with 'assert' statement
@@ -27,20 +27,18 @@ import org.jetbrains.kotlin.psi.psiUtil.replaced
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
public class SimplifyBooleanWithConstantsIntention : JetSelfTargetingOffsetIndependentIntention<JetBinaryExpression>( public class SimplifyBooleanWithConstantsIntention : JetSelfTargetingOffsetIndependentIntention<JetBinaryExpression>(javaClass(), "Simplify boolean expression") {
"simplify.boolean.with.constants", javaClass()) {
private var topParent : JetBinaryExpression? = null
override fun isApplicableTo(element: JetBinaryExpression): Boolean { override fun isApplicableTo(element: JetBinaryExpression): Boolean {
topParent = PsiTreeUtil.getTopmostParentOfType(element, javaClass<JetBinaryExpression>()) ?: element val topBinary = PsiTreeUtil.getTopmostParentOfType(element, javaClass<JetBinaryExpression>()) ?: element
return areThereExpressionsToBeSimplified(topParent) return areThereExpressionsToBeSimplified(topBinary)
} }
private fun areThereExpressionsToBeSimplified(element: JetExpression?) : Boolean { private fun areThereExpressionsToBeSimplified(element: JetExpression?) : Boolean {
if (element == null) return false if (element == null) return false
when (element) { when (element) {
is JetParenthesizedExpression -> return areThereExpressionsToBeSimplified(element.getExpression()) is JetParenthesizedExpression -> return areThereExpressionsToBeSimplified(element.getExpression())
is JetBinaryExpression -> { is JetBinaryExpression -> {
val op = element.getOperationToken() val op = element.getOperationToken()
if ((op == JetTokens.ANDAND || op == JetTokens.OROR) && if ((op == JetTokens.ANDAND || op == JetTokens.OROR) &&
@@ -52,76 +50,75 @@ public class SimplifyBooleanWithConstantsIntention : JetSelfTargetingOffsetIndep
} }
override fun applyTo(element: JetBinaryExpression, editor: Editor) { override fun applyTo(element: JetBinaryExpression, editor: Editor) {
// we know from isApplicableTo that topParent is not null val topBinary = PsiTreeUtil.getTopmostParentOfType(element, javaClass<JetBinaryExpression>()) ?: element
val simplified = toSimplifiedExpression(topParent!!) val simplified = toSimplifiedExpression(topBinary)
if (simplified is JetParenthesizedExpression) { topBinary.replace(JetPsiUtil.safeDeparenthesize(simplified))
val expr = simplified.getExpression()
if (expr != null) {
// this extra check is for the case where there are empty parentheses ()
topParent!!.replace(expr)
return
}
}
topParent!!.replace(simplified)
} }
private fun toSimplifiedExpression(element: JetExpression): JetExpression { private fun toSimplifiedExpression(expression: JetExpression): JetExpression {
val psiFactory = JetPsiFactory(element) val psiFactory = JetPsiFactory(expression)
if (element.canBeReducedToTrue())
return psiFactory.createExpression("true") when {
if (element.canBeReducedToFalse()) expression.canBeReducedToTrue() -> {
return psiFactory.createExpression("false") return psiFactory.createExpression("true")
when (element) { }
is JetParenthesizedExpression -> {
val expr = element.getExpression() expression.canBeReducedToFalse() -> {
if (expr == null) return element return psiFactory.createExpression("false")
val innerSimplified = toSimplifiedExpression(expr) }
if (innerSimplified is JetBinaryExpression) {
val simpText = innerSimplified.getText() ?: return element.copied() expression is JetParenthesizedExpression -> {
// wrap in new parentheses to keep the user's original format val expr = expression.getExpression()
return psiFactory.createExpression("($simpText)") if (expr != null) {
val simplified = toSimplifiedExpression(expr)
return if (simplified is JetBinaryExpression) {
// wrap in new parentheses to keep the user's original format
psiFactory.createExpressionByPattern("($0)", simplified)
}
else {
// if we now have a simpleName, constant, or parenthesized we don't need parentheses
simplified
}
} }
// if we now have a simpleName, constant, or parenthesized we don't need parentheses
return innerSimplified
} }
is JetBinaryExpression -> {
val left = element.getLeft()
val right = element.getRight()
val op = element.getOperationToken()
if (left == null || right == null || op == null || (op != JetTokens.ANDAND && op != JetTokens.OROR))
return element.copied()
val simpleLeft = simplifyExpression(left) expression is JetBinaryExpression -> {
val simpleRight = simplifyExpression(right) val left = expression.getLeft()
if (simpleLeft.canBeReducedToTrue() || simpleLeft.canBeReducedToFalse()) val right = expression.getRight()
return toSimplifiedBooleanBinaryExpressionWithConstantOperand(simpleLeft, simpleRight, op) val op = expression.getOperationToken()
if (simpleRight.canBeReducedToTrue() || simpleRight.canBeReducedToFalse()) if (left != null && right != null && (op == JetTokens.ANDAND || op == JetTokens.OROR)) {
return toSimplifiedBooleanBinaryExpressionWithConstantOperand(simpleRight, simpleLeft, op) val simpleLeft = simplifyExpression(left)
val simpleRight = simplifyExpression(right)
when {
simpleLeft.canBeReducedToTrue() -> return toSimplifiedBooleanBinaryExpressionWithConstantOperand(true, simpleRight, op)
val opText = element.getOperationReference().getText() ?: return element.copied() simpleLeft.canBeReducedToFalse() -> return toSimplifiedBooleanBinaryExpressionWithConstantOperand(false, simpleRight, op)
return psiFactory.createExpressionByPattern("$0 $opText $1", simpleLeft, simpleRight)
simpleRight.canBeReducedToTrue() -> return toSimplifiedBooleanBinaryExpressionWithConstantOperand(true, simpleLeft, op)
simpleRight.canBeReducedToFalse() -> return toSimplifiedBooleanBinaryExpressionWithConstantOperand(false, simpleLeft, op)
else -> {
val opText = expression.getOperationReference().getText()
return psiFactory.createExpressionByPattern("$0 $opText $1", simpleLeft, simpleRight)
}
}
}
} }
else -> return element.copied() }
return expression.copied()
}
private fun toSimplifiedBooleanBinaryExpressionWithConstantOperand(constantOperand: Boolean, otherOperand: JetExpression, operation: IElementType): JetExpression {
return when {
constantOperand && operation == JetTokens.OROR -> JetPsiFactory(otherOperand).createExpression("true")
!constantOperand && operation == JetTokens.ANDAND -> JetPsiFactory(otherOperand).createExpression("false")
else -> toSimplifiedExpression(otherOperand)
} }
} }
private fun toSimplifiedBooleanBinaryExpressionWithConstantOperand( private fun simplifyExpression(element: JetExpression) = element.replaced(toSimplifiedExpression(element))
booleanConstantOperand: JetExpression,
otherOperand: JetExpression,
operation: IElementType
): JetExpression {
assert(booleanConstantOperand.canBeReducedToBooleanConstant(null), "should only be called when we know it can be reduced")
val psiFactory = JetPsiFactory(otherOperand)
if (booleanConstantOperand.canBeReducedToTrue() && operation == JetTokens.OROR)
return psiFactory.createExpression("true")
if (booleanConstantOperand.canBeReducedToFalse() && operation == JetTokens.ANDAND)
return psiFactory.createExpression("false")
return toSimplifiedExpression(otherOperand)
}
private fun simplifyExpression(element: JetExpression): JetExpression {
return element.replaced(toSimplifiedExpression(element))
}
private fun JetExpression.canBeReducedToBooleanConstant(constant: Boolean?): Boolean { private fun JetExpression.canBeReducedToBooleanConstant(constant: Boolean?): Boolean {
val bindingContext = this.analyze() val bindingContext = this.analyze()
@@ -129,11 +126,7 @@ public class SimplifyBooleanWithConstantsIntention : JetSelfTargetingOffsetIndep
return CompileTimeConstantUtils.canBeReducedToBooleanConstant(this, trace, constant) return CompileTimeConstantUtils.canBeReducedToBooleanConstant(this, trace, constant)
} }
private fun JetExpression.canBeReducedToTrue(): Boolean { private fun JetExpression.canBeReducedToTrue() = canBeReducedToBooleanConstant(true)
return this.canBeReducedToBooleanConstant(true)
}
private fun JetExpression.canBeReducedToFalse(): Boolean { private fun JetExpression.canBeReducedToFalse() = canBeReducedToBooleanConstant(false)
return this.canBeReducedToBooleanConstant(false)
}
} }