OperatorToFunctionIntention and SwapBinaryExpressionIntention: more narrow availability range + no i18n
This commit is contained in:
@@ -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
|
||||
|
||||
+35
-17
@@ -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)
|
||||
}
|
||||
|
||||
+37
-29
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user