OperatorToFunctionIntention and SwapBinaryExpressionIntention: more narrow availability range + no i18n

This commit is contained in:
Valentin Kipyatkov
2015-04-14 15:10:51 +03:00
parent 00a4beae1d
commit dc6c6f13d5
25 changed files with 117 additions and 91 deletions
@@ -296,8 +296,6 @@ remove.unnecessary.curly.brackets.from.string.template=Remove curly braces from
remove.unnecessary.curly.brackets.from.string.template.family=Remove Curly Braces From Variable
insert.curly.brackets.to.string.template=Insert curly braces around variable
insert.curly.brackets.to.string.template.family=Insert Curly Braces Around Variable
swap.binary.expression=Flip binary expression
swap.binary.expression.family=Flip Binary Expression
add.name.to.argument.family=Add Name to Argument
add.name.to.argument.single=Add name to argument\: ''{0}''
add.name.to.argument.multiple=Add name to argument...
@@ -349,8 +347,6 @@ make.type.implicit.in.lambda=Make types implicit in lambda (may break code)
make.type.implicit.in.lambda.family=Make Types Implicit In Lambda (May Break Code)
invert.if.condition=Invert If Condition
invert.if.condition.family=Invert If Condition
operator.to.function=Replace overloaded operator with function call
operator.to.function.family=Replace Overloaded Operator With Function Call
convert.to.for.each.loop.intention=Replace with a for each loop
convert.to.for.each.loop.intention.family=Replace with a For Each Loop
convert.to.for.each.function.call.intention=Replace with a forEach function call
@@ -16,21 +16,24 @@
package org.jetbrains.kotlin.idea.intentions
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.psi.JetElement
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.idea.JetBundle
import org.jetbrains.kotlin.psi.JetElement
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypesAndPredicate
import com.intellij.codeInsight.intention.IntentionAction
import org.jetbrains.kotlin.psi.psiUtil.parents
public abstract class JetSelfTargetingIntention<T: JetElement>(
public val elementType: Class<T>,
public abstract class JetSelfTargetingIntention<TElement : JetElement>(
public val elementType: Class<TElement>,
private var text: String,
private val familyName: String)
private val familyName: String = text)
: IntentionAction {
deprecated("Use primary constructor, no need to use i18n")
public constructor(key: String, elementType: Class<T>) : this(elementType, JetBundle.message(key), JetBundle.message(key + ".family")) {
public constructor(key: String, elementType: Class<TElement>) : this(elementType, JetBundle.message(key), JetBundle.message(key + ".family")) {
}
protected fun setText(text: String) {
@@ -40,13 +43,28 @@ public abstract class JetSelfTargetingIntention<T: JetElement>(
final override fun getText() = text
final override fun getFamilyName() = familyName
public abstract fun isApplicableTo(element: T, caretOffset: Int): Boolean
public abstract fun isApplicableTo(element: TElement, caretOffset: Int): Boolean
public abstract fun applyTo(element: T, editor: Editor)
public abstract fun applyTo(element: TElement, editor: Editor)
private fun getTarget(editor: Editor, file: PsiFile): T? {
private fun getTarget(editor: Editor, file: PsiFile): TElement? {
val offset = editor.getCaretModel().getOffset()
return file.findElementAt(offset)?.getParentOfTypesAndPredicate(false, elementType) { element -> isApplicableTo(element, offset) }
val leaf1 = file.findElementAt(offset)
val leaf2 = file.findElementAt(offset - 1)
val commonParent = if (leaf1 != null && leaf2 != null) PsiTreeUtil.findCommonParent(leaf1, leaf2) else null
var elementsToCheck: Sequence<PsiElement> = sequence { null }
if (leaf1 != null) {
elementsToCheck += leaf1.parents().takeWhile { it != commonParent }
}
if (leaf2 != null) {
elementsToCheck += leaf2.parents().takeWhile { it != commonParent }
}
if (commonParent != null) {
elementsToCheck += commonParent.parents()
}
return elementsToCheck.filterIsInstance(elementType).firstOrNull { isApplicableTo(it, offset) }
}
final override fun isAvailable(project: Project, editor: Editor, file: PsiFile)
@@ -62,17 +80,17 @@ public abstract class JetSelfTargetingIntention<T: JetElement>(
override fun toString(): String = getText()
}
public abstract class JetSelfTargetingOffsetIndependentIntention<T: JetElement>(
elementType: Class<T>,
public abstract class JetSelfTargetingOffsetIndependentIntention<TElement : JetElement>(
elementType: Class<TElement>,
text: String,
familyName: String)
: JetSelfTargetingIntention<T>(elementType, text, familyName) {
familyName: String = text)
: JetSelfTargetingIntention<TElement>(elementType, text, familyName) {
deprecated("Use primary constructor, no need to use i18n")
public constructor(key: String, elementType: Class<T>) : this(elementType, JetBundle.message(key), JetBundle.message(key + ".family")) {
public constructor(key: String, elementType: Class<TElement>) : this(elementType, JetBundle.message(key), JetBundle.message(key + ".family")) {
}
public abstract fun isApplicableTo(element: T): Boolean
public abstract fun isApplicableTo(element: TElement): Boolean
override final fun isApplicableTo(element: T, caretOffset: Int): Boolean = isApplicableTo(element)
override final fun isApplicableTo(element: TElement, caretOffset: Int): Boolean = isApplicableTo(element)
}
@@ -16,49 +16,57 @@
package org.jetbrains.kotlin.idea.intentions
import org.jetbrains.kotlin.psi.JetExpression
import com.intellij.openapi.editor.Editor
import org.jetbrains.kotlin.psi.JetPrefixExpression
import org.jetbrains.kotlin.psi.JetPostfixExpression
import org.jetbrains.kotlin.psi.JetBinaryExpression
import org.jetbrains.kotlin.psi.JetArrayAccessExpression
import org.jetbrains.kotlin.psi.JetCallExpression
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.JetPsiFactory
import org.jetbrains.kotlin.psi.JetElement
import org.jetbrains.kotlin.psi.JetDotQualifiedExpression
import org.jetbrains.kotlin.resolve.BindingContext
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
public class OperatorToFunctionIntention : JetSelfTargetingOffsetIndependentIntention<JetExpression>("operator.to.function", javaClass()) {
public class OperatorToFunctionIntention : JetSelfTargetingIntention<JetExpression>(javaClass(), "Replace overloaded operator with function call") {
companion object {
private fun isApplicablePrefix(element: JetPrefixExpression): Boolean {
return when (element.getOperationReference().getReferencedNameElementType()) {
private fun isApplicablePrefix(element: JetPrefixExpression, caretOffset: Int): Boolean {
val opRef = element.getOperationReference()
if (!opRef.getTextRange().containsOffset(caretOffset)) return false
return when (opRef.getReferencedNameElementType()) {
JetTokens.PLUS, JetTokens.MINUS, JetTokens.PLUSPLUS, JetTokens.MINUSMINUS, JetTokens.EXCL -> true
else -> false
}
}
private fun isApplicablePostfix(element: JetPostfixExpression): Boolean {
return when (element.getOperationReference().getReferencedNameElementType()) {
private fun isApplicablePostfix(element: JetPostfixExpression, caretOffset: Int): Boolean {
val opRef = element.getOperationReference()
if (!opRef.getTextRange().containsOffset(caretOffset)) return false
return when (opRef.getReferencedNameElementType()) {
JetTokens.PLUSPLUS, JetTokens.MINUSMINUS -> true
else -> false
}
}
private fun isApplicableBinary(element: JetBinaryExpression): Boolean {
return when (element.getOperationReference().getReferencedNameElementType()) {
private fun isApplicableBinary(element: JetBinaryExpression, caretOffset: Int): Boolean {
val opRef = element.getOperationReference()
if (!opRef.getTextRange().containsOffset(caretOffset)) return false
return when (opRef.getReferencedNameElementType()) {
JetTokens.PLUS, JetTokens.MINUS, JetTokens.MUL, JetTokens.DIV, JetTokens.PERC, JetTokens.RANGE, JetTokens.IN_KEYWORD, JetTokens.NOT_IN, JetTokens.PLUSEQ, JetTokens.MINUSEQ, JetTokens.MULTEQ, JetTokens.DIVEQ, JetTokens.PERCEQ, JetTokens.EQEQ, JetTokens.EXCLEQ, JetTokens.GT, JetTokens.LT, JetTokens.GTEQ, JetTokens.LTEQ -> true
JetTokens.EQ -> element.getLeft() is JetArrayAccessExpression
else -> false
}
}
private fun isApplicableCall(element: JetCallExpression): Boolean {
val bindingContext = element.analyze()
val resolvedCall = element.getResolvedCall(bindingContext)
private fun isApplicableArrayAccess(element: JetArrayAccessExpression, caretOffset: Int): Boolean {
val lbracket = element.getLeftBracket() ?: return false
return lbracket.getTextRange().containsOffset(caretOffset)
}
private fun isApplicableCall(element: JetCallExpression, caretOffset: Int): Boolean {
val lbrace = (element.getValueArgumentList()?.getLeftParenthesis()
?: element.getFunctionLiteralArguments().firstOrNull()?.getFunctionLiteral()?.getLeftCurlyBrace()
?: return false) as PsiElement
if (!lbrace.getTextRange().containsOffset(caretOffset)) return false
val resolvedCall = element.getResolvedCall(element.analyze())
val descriptor = resolvedCall?.getResultingDescriptor()
if (descriptor is FunctionDescriptor && descriptor.getName().asString() == "invoke") {
val parent = element.getParent()
@@ -110,7 +118,7 @@ public class OperatorToFunctionIntention : JetSelfTargetingOffsetIndependentInte
if (op == JetTokens.EQ) {
if (left is JetArrayAccessExpression) {
convertArrayAccess(left as JetArrayAccessExpression)
convertArrayAccess(left)
}
return element
}
@@ -195,13 +203,13 @@ public class OperatorToFunctionIntention : JetSelfTargetingOffsetIndependentInte
}
}
override fun isApplicableTo(element: JetExpression): Boolean {
override fun isApplicableTo(element: JetExpression, caretOffset: Int): Boolean {
return when (element) {
is JetPrefixExpression -> isApplicablePrefix(element)
is JetPostfixExpression -> isApplicablePostfix(element)
is JetBinaryExpression -> isApplicableBinary(element)
is JetArrayAccessExpression -> true
is JetCallExpression -> isApplicableCall(element)
is JetPrefixExpression -> isApplicablePrefix(element, caretOffset)
is JetPostfixExpression -> isApplicablePostfix(element, caretOffset)
is JetBinaryExpression -> isApplicableBinary(element, caretOffset)
is JetArrayAccessExpression -> isApplicableArrayAccess(element, caretOffset)
is JetCallExpression -> isApplicableCall(element, caretOffset)
else -> false
}
}