Refactored SplitIfIntention + JetPsiFactory.createIf uses createExpressionByPattern and does not reformat its arguments

This commit is contained in:
Valentin Kipyatkov
2015-05-05 14:06:19 +03:00
parent de8601bb5d
commit 7499c4bc19
14 changed files with 49 additions and 78 deletions
@@ -349,8 +349,11 @@ public class JetPsiFactory(private val project: Project) {
return createReturn(JetPsiUtil.getText(expression)) return createReturn(JetPsiUtil.getText(expression))
} }
public fun createIf(condition: JetExpression?, thenExpr: JetExpression?, elseExpr: JetExpression?): JetIfExpression { public fun createIf(condition: JetExpression, thenExpr: JetExpression, elseExpr: JetExpression?): JetIfExpression {
return createExpression(JetPsiUnparsingUtils.toIf(condition, thenExpr, elseExpr)) as 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 { public fun createArgumentWithName(name: String?, argumentExpression: JetExpression): JetValueArgument {
@@ -23,20 +23,6 @@ public class JetPsiUnparsingUtils {
private 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 @NotNull
public static String toBinaryExpression(@Nullable JetExpression left, @NotNull String op, @Nullable JetElement right) { public static String toBinaryExpression(@Nullable JetExpression left, @NotNull String op, @Nullable JetElement right) {
return toBinaryExpression(JetPsiUtil.getText(left), op, JetPsiUtil.getText(right)); return toBinaryExpression(JetPsiUtil.getText(left), op, JetPsiUtil.getText(right));
@@ -80,9 +80,6 @@ public fun JetPsiFactory.createExpressionByPattern(pattern: String, vararg args:
bound = range.getStartOffset() + start bound = range.getStartOffset() + start
} }
expression = codeStyleManager.reformatRange(expression, start, bound + 1, true) as JetExpression 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 // do not reformat the whole expression in PostprocessReformattingAspect
@@ -97,6 +94,8 @@ public fun JetPsiFactory.createExpressionByPattern(pattern: String, vararg args:
element.replace(arg) element.replace(arg)
} }
codeStyleManager.adjustLineIndent(expression.getContainingFile(), expression.getTextRange())
return expression return expression
} }
@@ -46,8 +46,7 @@ import org.jetbrains.kotlin.JetNodeTypes
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
public fun JetCallElement.getCallNameExpression(): JetSimpleNameExpression? { public fun JetCallElement.getCallNameExpression(): JetSimpleNameExpression? {
val calleeExpression = getCalleeExpression() val calleeExpression = getCalleeExpression() ?: return null
if (calleeExpression == null) return null
return when (calleeExpression) { return when (calleeExpression) {
is JetSimpleNameExpression -> calleeExpression is JetSimpleNameExpression -> calleeExpression
@@ -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.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
split.if=Split into 2 if's
split.if.family=Split If
simplify.boolean.with.constants=Simplify boolean expression simplify.boolean.with.constants=Simplify boolean expression
simplify.boolean.with.constants.family=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
@@ -68,7 +68,10 @@ public class KotlinUnwrappers {
@Override @Override
public boolean isApplicableTo(PsiElement e) { 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 @Override
@@ -16,98 +16,74 @@
package org.jetbrains.kotlin.idea.intentions package org.jetbrains.kotlin.idea.intentions
import org.jetbrains.kotlin.psi.JetIfExpression
import com.intellij.openapi.editor.Editor 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.lexer.JetTokens
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.psi.JetSimpleNameExpression import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.JetExpression
import org.jetbrains.kotlin.psi.JetPsiUtil
public class SplitIfIntention : JetSelfTargetingIntention<JetExpression>("split.if", javaClass()) { public class SplitIfIntention : JetSelfTargetingIntention<JetExpression>(javaClass(), "Split if into 2 if's") {
override fun isApplicableTo(element: JetExpression, caretOffset: Int): Boolean { override fun isApplicableTo(element: JetExpression, caretOffset: Int): Boolean {
if (element !is JetSimpleNameExpression && element !is JetIfExpression) return false return when (element) {
is JetSimpleNameExpression -> isOperatorValid(element)
if (element is JetSimpleNameExpression) { is JetIfExpression -> getFirstValidOperator(element) != null && element.getIfKeyword().getTextRange().containsOffset(caretOffset)
return isOperatorValid(element) 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) { override fun applyTo(element: JetExpression, editor: Editor) {
val currentElement = when (element) { val operator = when (element) {
is JetIfExpression -> getFirstValidOperator(element) is JetIfExpression -> getFirstValidOperator(element)!!
else -> element as JetSimpleNameExpression else -> element as JetSimpleNameExpression
} }
val ifExpression = currentElement!!.getNonStrictParentOfType<JetIfExpression>() val ifExpression = operator.getNonStrictParentOfType<JetIfExpression>()
val expression = currentElement.getParent() as JetBinaryExpression val expression = operator.getParent() as JetBinaryExpression
val rightExpression = getRight(expression, ifExpression!!.getCondition()) val rightExpression = JetPsiUtil.safeDeparenthesize(getRight(expression, ifExpression!!.getCondition()!!))
val leftExpression = expression.getLeft() val leftExpression = JetPsiUtil.safeDeparenthesize(expression.getLeft()!!)
val thenExpression = ifExpression.getThen()!!
val elseExpression = ifExpression.getElse() val elseExpression = ifExpression.getElse()
val thenExpression = ifExpression.getThen()
val psiFactory = JetPsiFactory(element) val psiFactory = JetPsiFactory(element)
if (currentElement.getReferencedNameElementType() == JetTokens.ANDAND) { val innerIf = psiFactory.createIf(rightExpression, thenExpression, elseExpression)
ifExpression.replace( val newIf = when (operator.getReferencedNameElementType()) {
psiFactory.createIf(leftExpression, psiFactory.wrapInABlock( JetTokens.ANDAND -> psiFactory.createIf(leftExpression, psiFactory.wrapInABlock(innerIf), elseExpression)
psiFactory.createIf(rightExpression, thenExpression, elseExpression)
), JetTokens.OROR -> psiFactory.createIf(leftExpression, thenExpression, innerIf)
elseExpression)
) else -> throw IllegalArgumentException()
}
else {
ifExpression.replace(psiFactory.createIf(leftExpression, thenExpression,
psiFactory.createIf(rightExpression, thenExpression, elseExpression))
)
} }
ifExpression.replace(newIf)
} }
private fun getRight(element: JetBinaryExpression, condition: JetExpression): JetExpression { private fun getRight(element: JetBinaryExpression, condition: JetExpression): JetExpression {
//gets the textOffset of the right side of the JetBinaryExpression in context to condition //gets the textOffset of the right side of the JetBinaryExpression in context to condition
val startOffset = element.getRight()!!.getTextOffset() - condition.getTextOffset() 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) 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? { private fun getFirstValidOperator(element: JetIfExpression): JetSimpleNameExpression? {
if (element.getCondition() == null) return null val condition = element.getCondition() ?: return null
val condition = element.getCondition() return PsiTreeUtil.findChildrenOfType(condition, javaClass<JetSimpleNameExpression>())
val childElements = PsiTreeUtil.findChildrenOfType(condition, javaClass<JetSimpleNameExpression>()) .firstOrNull { isOperatorValid(it) }
return childElements.firstOrNull { isOperatorValid(it) }
} }
private fun isOperatorValid(element: JetSimpleNameExpression): Boolean { private fun isOperatorValid(element: JetSimpleNameExpression): Boolean {
val operator = element.getReferencedNameElementType() val operator = element.getReferencedNameElementType()
if (operator != JetTokens.ANDAND && operator != JetTokens.OROR) return false if (operator != JetTokens.ANDAND && operator != JetTokens.OROR) return false
if (element.getParent() !is JetBinaryExpression) return false var expression = element.getParent() as? JetBinaryExpression ?: return false
var expression = element.getParent() as JetBinaryExpression
if (expression.getRight() == null || expression.getLeft() == null) return false if (expression.getRight() == null || expression.getLeft() == null) return false
while (expression.getParent() is JetBinaryExpression) { while (true) {
expression = expression.getParent() as JetBinaryExpression expression = expression.getParent() as? JetBinaryExpression ?: break
if (operator == JetTokens.ANDAND && expression.getOperationToken() != JetTokens.ANDAND) return false if (expression.getOperationToken() != operator) return false
if (operator == JetTokens.OROR && expression.getOperationToken() != JetTokens.OROR) return false
} }
if (expression.getParent()?.getParent() !is JetIfExpression) return false val ifExpression = expression.getParent()?.getParent() as? JetIfExpression ?: return false
val ifExpression = expression.getParent()?.getParent() as JetIfExpression
if (ifExpression.getCondition() == null) return false if (ifExpression.getCondition() == null) return false
if (!PsiTreeUtil.isAncestor(ifExpression.getCondition(), element, false)) return false if (!PsiTreeUtil.isAncestor(ifExpression.getCondition(), element, false)) return false
@@ -2,6 +2,7 @@
// ACTION: Create class 'A' // ACTION: Create class 'A'
// ACTION: Create interface 'A' // ACTION: Create interface 'A'
// ACTION: Convert to block body // ACTION: Convert to block body
// ACTION: Remove explicit type specification
// ERROR: Unresolved reference: A // ERROR: Unresolved reference: A
package p package p
@@ -4,6 +4,7 @@
// ACTION: Create interface 'A' // ACTION: Create interface 'A'
// ACTION: Create enum 'A' // ACTION: Create enum 'A'
// ACTION: Convert to block body // ACTION: Convert to block body
// ACTION: Remove explicit type specification
// ERROR: Unresolved reference: A // ERROR: Unresolved reference: A
package p package p
@@ -1,5 +1,6 @@
// "Create class 'A'" "false" // "Create class 'A'" "false"
// ACTION: Convert to block body // ACTION: Convert to block body
// ACTION: Remove explicit type specification
// ERROR: Unresolved reference: A // ERROR: Unresolved reference: A
package p package p
@@ -4,6 +4,7 @@
// ACTION: Create interface 'A' // ACTION: Create interface 'A'
// ACTION: Create enum 'A' // ACTION: Create enum 'A'
// ACTION: Convert to block body // ACTION: Convert to block body
// ACTION: Remove explicit type specification
// ERROR: Unresolved reference: A // ERROR: Unresolved reference: A
package p package p
@@ -2,6 +2,7 @@
// ACTION: Create class 'A' // ACTION: Create class 'A'
// ACTION: Create interface 'A' // ACTION: Create interface 'A'
// ACTION: Convert to block body // ACTION: Convert to block body
// ACTION: Remove explicit type specification
// ERROR: Unresolved reference: A // ERROR: Unresolved reference: A
package p package p
@@ -4,6 +4,7 @@
// ACTION: Create interface 'A' // ACTION: Create interface 'A'
// ACTION: Create enum 'A' // ACTION: Create enum 'A'
// ACTION: Convert to block body // ACTION: Convert to block body
// ACTION: Remove explicit type specification
// ERROR: Unresolved reference: A // ERROR: Unresolved reference: A
package p package p
@@ -2,6 +2,7 @@
// ACTION: Create class 'A' // ACTION: Create class 'A'
// ACTION: Create interface 'A' // ACTION: Create interface 'A'
// ACTION: Convert to block body // ACTION: Convert to block body
// ACTION: Remove explicit type specification
// ERROR: Unresolved reference: A // ERROR: Unresolved reference: A
package p package p