Refactored intentions to replace special name calls with operators
This commit is contained in:
@@ -88,22 +88,6 @@ replace.call.error.invalid.arguments=Invalid arguments included.
|
||||
replace.call.error.vararg.not.last=Cannot handle named arguments after a vararg.
|
||||
replace.call.error.contains.returns_boolean=Contains must return a Boolean.
|
||||
|
||||
replace.get.with.index=Replace 'get' call with index operator
|
||||
replace.get.with.index.family=Replace Get
|
||||
|
||||
replace.contains.with.in=Replace 'contains' call with in operator
|
||||
replace.contains.with.in.family=Replace Contains
|
||||
|
||||
replace.invoke.with.call=Replace 'invoke' with direct call
|
||||
replace.invoke.with.call.family=Replace Invoke
|
||||
|
||||
replace.unary.operator.with.prefix=Replace with ''{0}'' prefix
|
||||
replace.unary.operator.with.prefix.family=Replace Unary Operator
|
||||
|
||||
replace.binary.operator.with.infix=Replace with ''{0}'' operator
|
||||
replace.binary.operator.with.infix.family=Replace Binary Operator
|
||||
|
||||
|
||||
add.kotlin.signature.action.family.name=Specify Custom Kotlin Signature
|
||||
add.kotlin.signature.action.text=Specify custom Kotlin signature
|
||||
edit.kotlin.signature.action.text=Edit custom Kotlin signature
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.idea.intentions
|
||||
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import org.jetbrains.kotlin.JetNodeTypes
|
||||
import org.jetbrains.kotlin.analyzer.analyzeInContext
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
@@ -31,8 +30,9 @@ import org.jetbrains.kotlin.lexer.JetTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.typeRefHelpers.setReceiverTypeReference
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.types.JetType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
|
||||
fun JetCallableDeclaration.setType(type: JetType) {
|
||||
if (type.isError()) return
|
||||
@@ -102,3 +102,15 @@ fun splitPropertyDeclaration(property: JetProperty): JetBinaryExpression {
|
||||
|
||||
return assignment
|
||||
}
|
||||
|
||||
val JetQualifiedExpression.callExpression: JetCallExpression?
|
||||
get() = getSelectorExpression() as? JetCallExpression
|
||||
|
||||
val JetQualifiedExpression.functionName: String?
|
||||
get() = (callExpression?.getCalleeExpression() as? JetSimpleNameExpression)?.getText()
|
||||
|
||||
fun JetQualifiedExpression.toResolvedCall(): ResolvedCall<out CallableDescriptor>? {
|
||||
val callExpression = callExpression ?: return null
|
||||
return callExpression.getResolvedCall(callExpression.analyze()) ?: return null
|
||||
}
|
||||
|
||||
|
||||
-137
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.intentions.attributeCallReplacements
|
||||
|
||||
import com.intellij.codeInsight.hint.HintManager
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.idea.JetBundle
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingOffsetIndependentIntention
|
||||
import org.jetbrains.kotlin.idea.util.Maybe
|
||||
import org.jetbrains.kotlin.idea.util.MaybeError
|
||||
import org.jetbrains.kotlin.idea.util.MaybeValue
|
||||
import org.jetbrains.kotlin.psi.JetCallExpression
|
||||
import org.jetbrains.kotlin.psi.JetDotQualifiedExpression
|
||||
import org.jetbrains.kotlin.psi.JetQualifiedExpression
|
||||
import org.jetbrains.kotlin.psi.ValueArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument
|
||||
|
||||
// Internal because you shouldn't construct this manually. You can end up with an inconsistant CallDescription.
|
||||
public class CallDescription internal (
|
||||
public val element: JetQualifiedExpression,
|
||||
public val callElement: JetCallExpression,
|
||||
public val resolved: ResolvedCall<out CallableDescriptor>
|
||||
) {
|
||||
public val functionName: String?
|
||||
get() = callElement.getCalleeExpression()?.getText()
|
||||
|
||||
public val argumentCount: Int
|
||||
get() = callElement.getValueArguments().size
|
||||
|
||||
public val hasTypeArguments: Boolean
|
||||
get() = callElement.getTypeArgumentList() != null
|
||||
|
||||
public val hasEmptyArguments: Boolean
|
||||
get() = callElement.getValueArguments().any { it?.getArgumentExpression() == null }
|
||||
|
||||
public fun getPositionalArguments(): Maybe<List<ValueArgument>, String> {
|
||||
|
||||
val resolvedValueArguments = resolved.getValueArgumentsByIndex()
|
||||
?: return MaybeError("duplicate.or.missing.arguments")
|
||||
|
||||
// Check for mixed default and passed arguments and return the passed parameters (or fail)
|
||||
val indexOfFirstDefaultArgument = resolvedValueArguments.indexOf(DefaultValueArgument.DEFAULT)
|
||||
val valueArgumentGroups = if (indexOfFirstDefaultArgument >= 0) {
|
||||
if (resolvedValueArguments.listIterator(indexOfFirstDefaultArgument).any { it != DefaultValueArgument.DEFAULT }) {
|
||||
return MaybeError("skipped.defaults")
|
||||
}
|
||||
resolvedValueArguments.subList(0, indexOfFirstDefaultArgument)
|
||||
} else {
|
||||
resolvedValueArguments
|
||||
}
|
||||
|
||||
// The vararg must be the last (passed) argument
|
||||
if (valueArgumentGroups.size > 0) {
|
||||
val vararg = valueArgumentGroups.find { it is VarargValueArgument }
|
||||
if (vararg != null && vararg != valueArgumentGroups.last) {
|
||||
return MaybeError("vararg.not.last")
|
||||
}
|
||||
}
|
||||
|
||||
val valueArguments = valueArgumentGroups.flatMap { it.getArguments() }
|
||||
if (valueArguments.size < argumentCount) {
|
||||
// Only happens if invalid arguments were thrown away.
|
||||
return MaybeError("invalid.arguments")
|
||||
}
|
||||
|
||||
return MaybeValue(valueArguments)
|
||||
}
|
||||
}
|
||||
|
||||
public fun JetQualifiedExpression.toCallDescription(): CallDescription? {
|
||||
val callExpression = getSelectorExpression() as? JetCallExpression ?: return null
|
||||
|
||||
val bindingContext = callExpression.analyze()
|
||||
// This should work. Nothing that returns a CallableDescriptor returns null and (out T is T)
|
||||
val resolvedCall = callExpression.getResolvedCall(bindingContext) ?:
|
||||
return null
|
||||
|
||||
return CallDescription(this, callExpression, resolvedCall)
|
||||
}
|
||||
|
||||
public abstract class AttributeCallReplacementIntention(private val name: String) : JetSelfTargetingOffsetIndependentIntention<JetDotQualifiedExpression>(name, javaClass()) {
|
||||
|
||||
protected abstract fun isApplicableToCall(call: CallDescription): Boolean
|
||||
|
||||
protected abstract fun replaceCall(call: CallDescription, editor: Editor): Unit
|
||||
protected open fun formatArgumentsFor(call: CallDescription): Array<Any?> = array()
|
||||
|
||||
final override fun isApplicableTo(element: JetDotQualifiedExpression): Boolean {
|
||||
val callDescription = element.toCallDescription()
|
||||
if (callDescription != null && isApplicableToCall(callDescription)) {
|
||||
setText(JetBundle.message(name, *formatArgumentsFor(callDescription)))
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
final override fun applyTo(element: JetDotQualifiedExpression, editor: Editor) {
|
||||
// If this doesn't work, something is very wrong (isApplicableTo failed).
|
||||
replaceCall(element.toCallDescription()!!, editor)
|
||||
}
|
||||
|
||||
protected open fun intentionFailed(editor: Editor, messageId: String, vararg values: Any?) {
|
||||
val message = "Intention failed: ${JetBundle.message("replace.call.error.${messageId}", *values)}"
|
||||
HintManager.getInstance().showErrorHint(editor, message)
|
||||
}
|
||||
|
||||
protected fun handleErrors<V: Any>(editor: Editor, maybeValue: Maybe<V, String>): V? {
|
||||
return when (maybeValue) {
|
||||
is MaybeValue -> maybeValue.value
|
||||
is MaybeError -> {
|
||||
intentionFailed(editor, maybeValue.error)
|
||||
null
|
||||
}
|
||||
else -> throw NoWhenBranchMatchedException()
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
-27
@@ -17,13 +17,37 @@
|
||||
package org.jetbrains.kotlin.idea.intentions.attributeCallReplacements
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingOffsetIndependentIntention
|
||||
import org.jetbrains.kotlin.idea.intentions.callExpression
|
||||
import org.jetbrains.kotlin.idea.intentions.functionName
|
||||
import org.jetbrains.kotlin.idea.intentions.toResolvedCall
|
||||
import org.jetbrains.kotlin.psi.JetDotQualifiedExpression
|
||||
import org.jetbrains.kotlin.psi.JetPsiFactory
|
||||
import org.jetbrains.kotlin.psi.createExpressionByPattern
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
|
||||
|
||||
public open class ReplaceBinaryInfixIntention : AttributeCallReplacementIntention("replace.binary.operator.with.infix") {
|
||||
public class ReplaceBinaryInfixIntention : JetSelfTargetingOffsetIndependentIntention<JetDotQualifiedExpression>(javaClass(), "Replace call with binary operator") {
|
||||
override fun isApplicableTo(element: JetDotQualifiedExpression): Boolean {
|
||||
val operation = operation(element.functionName) ?: return false
|
||||
val resolvedCall = element.toResolvedCall() ?: return false
|
||||
if (!resolvedCall.getStatus().isSuccess()) return false
|
||||
if (resolvedCall.getCall().getTypeArgumentList() != null) return false
|
||||
val argument = resolvedCall.getCall().getValueArguments().singleOrNull() ?: return false
|
||||
if ((resolvedCall.getArgumentMapping(argument) as ArgumentMatch).valueParameter.getIndex() != 0) return false
|
||||
setText("Replace with '$operation' operator")
|
||||
return true
|
||||
}
|
||||
|
||||
private fun lookup(name: String?): String? {
|
||||
return when (name) {
|
||||
override fun applyTo(element: JetDotQualifiedExpression, editor: Editor) {
|
||||
val operation = operation(element.functionName)!!
|
||||
val argument = element.callExpression!!.getValueArguments().single().getArgumentExpression()!!
|
||||
val receiver = element.getReceiverExpression()
|
||||
|
||||
element.replace(JetPsiFactory(element).createExpressionByPattern("$0 $operation $1", receiver, argument))
|
||||
}
|
||||
|
||||
private fun operation(functionName: String?): String? {
|
||||
return when (functionName) {
|
||||
"plus" -> "+"
|
||||
"minus" -> "-"
|
||||
"div" -> "/"
|
||||
@@ -33,28 +57,4 @@ public open class ReplaceBinaryInfixIntention : AttributeCallReplacementIntentio
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun formatArgumentsFor(call: CallDescription): Array<Any?> {
|
||||
return array(lookup(call.functionName))
|
||||
}
|
||||
|
||||
override fun isApplicableToCall(call: CallDescription): Boolean {
|
||||
return (
|
||||
lookup(call.functionName) != null &&
|
||||
call.argumentCount == 1 &&
|
||||
!call.hasTypeArguments &&
|
||||
!call.hasEmptyArguments
|
||||
)
|
||||
}
|
||||
|
||||
override fun replaceCall(call: CallDescription, editor: Editor) {
|
||||
val argument = (handleErrors(editor, call.getPositionalArguments()) ?: return)[0].getArgumentExpression()
|
||||
|
||||
val operation = lookup(call.functionName)!! // Lookup must succeed
|
||||
call.element.replace(
|
||||
JetPsiFactory(call.element).createExpressionByPattern("$0 $operation $1",
|
||||
call.element.getReceiverExpression(), argument
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+23
-22
@@ -16,34 +16,38 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.intentions.attributeCallReplacements
|
||||
|
||||
import org.jetbrains.kotlin.lexer.JetTokens
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingOffsetIndependentIntention
|
||||
import org.jetbrains.kotlin.idea.intentions.callExpression
|
||||
import org.jetbrains.kotlin.idea.intentions.functionName
|
||||
import org.jetbrains.kotlin.idea.intentions.toResolvedCall
|
||||
import org.jetbrains.kotlin.lexer.JetTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
|
||||
public open class ReplaceContainsIntention : AttributeCallReplacementIntention("replace.contains.with.in") {
|
||||
public class ReplaceContainsIntention : JetSelfTargetingOffsetIndependentIntention<JetDotQualifiedExpression>(javaClass(), "Replace 'contains' call with 'in' operator") {
|
||||
override fun isApplicableTo(element: JetDotQualifiedExpression): Boolean {
|
||||
if (element.functionName != OperatorConventions.CONTAINS.asString()) return false
|
||||
val resolvedCall = element.toResolvedCall() ?: return false
|
||||
if (!resolvedCall.getStatus().isSuccess()) return false
|
||||
val argument = resolvedCall.getCall().getValueArguments().singleOrNull() ?: return false
|
||||
if ((resolvedCall.getArgumentMapping(argument) as ArgumentMatch).valueParameter.getIndex() != 0) return false
|
||||
|
||||
override fun isApplicableToCall(call: CallDescription): Boolean {
|
||||
return call.functionName == "contains" && call.argumentCount == 1 && !call.hasEmptyArguments
|
||||
val target = resolvedCall.getResultingDescriptor()
|
||||
val returnType = target.getReturnType() ?: return false
|
||||
return target.builtIns.isBooleanOrSubtype(returnType)
|
||||
}
|
||||
|
||||
override fun replaceCall(call: CallDescription, editor: Editor) {
|
||||
val resultingDescriptor = call.resolved.getResultingDescriptor()
|
||||
val ret = resultingDescriptor.getReturnType()
|
||||
?: return intentionFailed(editor, "undefined.returntype")
|
||||
|
||||
if (!resultingDescriptor.builtIns.isBooleanOrSubtype(ret)) {
|
||||
return intentionFailed(editor, "contains.returns.boolean")
|
||||
}
|
||||
|
||||
val argument = (handleErrors(editor, call.getPositionalArguments()) ?: return)[0].getArgumentExpression()
|
||||
override fun applyTo(element: JetDotQualifiedExpression, editor: Editor) {
|
||||
val argument = element.callExpression!!.getValueArguments().single().getArgumentExpression()!!
|
||||
val receiver = element.getReceiverExpression()
|
||||
|
||||
// Append semicolon to previous statement if needed
|
||||
val psiFactory = JetPsiFactory(call.element)
|
||||
val psiFactory = JetPsiFactory(element)
|
||||
if (argument is JetFunctionLiteralExpression) {
|
||||
val previousElement = JetPsiUtil.skipSiblingsBackwardByPredicate(call.element) {
|
||||
val previousElement = JetPsiUtil.skipSiblingsBackwardByPredicate(element) {
|
||||
// I checked, it can't be null.
|
||||
it!!.getNode()?.getElementType() in JetTokens.WHITE_SPACE_OR_COMMENT_BIT_SET
|
||||
}
|
||||
@@ -53,9 +57,6 @@ public open class ReplaceContainsIntention : AttributeCallReplacementIntention("
|
||||
}
|
||||
}
|
||||
|
||||
call.element.replace(psiFactory.createExpressionByPattern("$0 in $1",
|
||||
argument,
|
||||
call.element.getReceiverExpression()
|
||||
))
|
||||
element.replace(psiFactory.createExpressionByPattern("$0 in $1", argument, receiver))
|
||||
}
|
||||
}
|
||||
|
||||
+31
-11
@@ -17,21 +17,41 @@
|
||||
package org.jetbrains.kotlin.idea.intentions.attributeCallReplacements
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingOffsetIndependentIntention
|
||||
import org.jetbrains.kotlin.idea.intentions.callExpression
|
||||
import org.jetbrains.kotlin.idea.intentions.functionName
|
||||
import org.jetbrains.kotlin.psi.JetDotQualifiedExpression
|
||||
import org.jetbrains.kotlin.psi.JetPsiFactory
|
||||
import org.jetbrains.kotlin.psi.buildExpression
|
||||
import org.jetbrains.kotlin.psi.createExpressionByPattern
|
||||
|
||||
public open class ReplaceGetIntention : AttributeCallReplacementIntention("replace.get.with.index") {
|
||||
|
||||
override fun isApplicableToCall(call: CallDescription): Boolean {
|
||||
return (call.functionName == "get" && !call.hasTypeArguments && call.argumentCount > 0)
|
||||
public class ReplaceGetIntention : JetSelfTargetingOffsetIndependentIntention<JetDotQualifiedExpression>(javaClass(), "Replace 'get' call with index operator") {
|
||||
override fun isApplicableTo(element: JetDotQualifiedExpression): Boolean {
|
||||
if (element.functionName != "get") return false
|
||||
val call = element.callExpression ?: return false
|
||||
if (call.getTypeArgumentList() != null) return false
|
||||
val arguments = call.getValueArguments()
|
||||
if (arguments.isEmpty()) return false
|
||||
if (arguments.any { it.isNamed() }) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun replaceCall(call: CallDescription, editor: Editor) {
|
||||
val argumentString = (handleErrors(editor, call.getPositionalArguments()) ?: return).map {
|
||||
it.getArgumentExpression()?.getText() ?: ""
|
||||
}.makeString(", ")
|
||||
override fun applyTo(element: JetDotQualifiedExpression, editor: Editor) {
|
||||
val expression = JetPsiFactory(element).buildExpression {
|
||||
appendExpression(element.getReceiverExpression())
|
||||
|
||||
call.element.replace(JetPsiFactory(call.element).createExpression(
|
||||
"${call.element.getReceiverExpression().getText()}[${argumentString}]"
|
||||
))
|
||||
appendFixedText("[")
|
||||
|
||||
val call = element.callExpression!!
|
||||
for ((index, argument) in call.getValueArguments().withIndex()) {
|
||||
if (index > 0) {
|
||||
appendFixedText(",")
|
||||
}
|
||||
appendExpression(argument.getArgumentExpression())
|
||||
}
|
||||
|
||||
appendFixedText("]")
|
||||
}
|
||||
element.replace(expression)
|
||||
}
|
||||
}
|
||||
|
||||
+13
-12
@@ -17,21 +17,22 @@
|
||||
package org.jetbrains.kotlin.idea.intentions.attributeCallReplacements
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import org.jetbrains.kotlin.psi.JetPsiFactory
|
||||
import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingOffsetIndependentIntention
|
||||
import org.jetbrains.kotlin.idea.intentions.callExpression
|
||||
import org.jetbrains.kotlin.idea.intentions.functionName
|
||||
import org.jetbrains.kotlin.psi.JetCallExpression
|
||||
import org.jetbrains.kotlin.psi.JetDotQualifiedExpression
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
|
||||
public open class ReplaceInvokeIntention : AttributeCallReplacementIntention("replace.invoke.with.call") {
|
||||
|
||||
override fun isApplicableToCall(call: CallDescription): Boolean {
|
||||
return call.functionName == OperatorConventions.INVOKE.asString()
|
||||
public class ReplaceInvokeIntention : JetSelfTargetingOffsetIndependentIntention<JetDotQualifiedExpression>(javaClass(), "Replace 'invoke' with direct call") {
|
||||
override fun isApplicableTo(element: JetDotQualifiedExpression): Boolean {
|
||||
return element.functionName == OperatorConventions.INVOKE.asString()
|
||||
}
|
||||
|
||||
override fun replaceCall(call: CallDescription, editor: Editor) {
|
||||
call.element.replace(JetPsiFactory(call.element).createExpression(
|
||||
call.element.getReceiverExpression().getText() +
|
||||
(call.callElement.getTypeArgumentList()?.getText() ?: "") +
|
||||
(call.callElement.getValueArgumentList()?.getText() ?: "") +
|
||||
(call.callElement.getFunctionLiteralArguments().fold("") { a, b -> a + " " + b.getText() })
|
||||
))
|
||||
override fun applyTo(element: JetDotQualifiedExpression, editor: Editor) {
|
||||
val receiver = element.getReceiverExpression()
|
||||
val callExpression = element.callExpression!!
|
||||
callExpression.getCalleeExpression()!!.replace(receiver)
|
||||
element.replace(callExpression)
|
||||
}
|
||||
}
|
||||
|
||||
+22
-22
@@ -17,35 +17,35 @@
|
||||
package org.jetbrains.kotlin.idea.intentions.attributeCallReplacements
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingOffsetIndependentIntention
|
||||
import org.jetbrains.kotlin.idea.intentions.callExpression
|
||||
import org.jetbrains.kotlin.idea.intentions.functionName
|
||||
import org.jetbrains.kotlin.psi.JetDotQualifiedExpression
|
||||
import org.jetbrains.kotlin.psi.JetPsiFactory
|
||||
import org.jetbrains.kotlin.psi.createExpressionByPattern
|
||||
|
||||
public open class ReplaceUnaryPrefixIntention : AttributeCallReplacementIntention("replace.unary.operator.with.prefix") {
|
||||
public class ReplaceUnaryPrefixIntention : JetSelfTargetingOffsetIndependentIntention<JetDotQualifiedExpression>(javaClass(), "Replace call with unary operator") {
|
||||
override fun isApplicableTo(element: JetDotQualifiedExpression): Boolean {
|
||||
val operation = operation(element.functionName) ?: return false
|
||||
val call = element.callExpression ?: return false
|
||||
if (call.getTypeArgumentList() != null) return false
|
||||
if (!call.getValueArguments().isEmpty()) return false
|
||||
setText("Replace with '$operation' operator")
|
||||
return true
|
||||
}
|
||||
|
||||
private fun lookup(name: String?) : String? {
|
||||
return when (name) {
|
||||
override fun applyTo(element: JetDotQualifiedExpression, editor: Editor) {
|
||||
val operation = operation(element.functionName)!!
|
||||
val receiver = element.getReceiverExpression()
|
||||
element.replace(JetPsiFactory(element).createExpressionByPattern("$0$1", operation, receiver))
|
||||
}
|
||||
|
||||
private fun operation(functionName: String?) : String? {
|
||||
return when (functionName) {
|
||||
"plus" -> "+"
|
||||
"minus" -> "-"
|
||||
"not" -> "!"
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun formatArgumentsFor(call: CallDescription): Array<Any?> {
|
||||
return array(lookup(call.functionName))
|
||||
}
|
||||
|
||||
override fun isApplicableToCall(call: CallDescription): Boolean {
|
||||
return (
|
||||
lookup(call.functionName) != null &&
|
||||
!call.hasTypeArguments &&
|
||||
call.argumentCount == 0 &&
|
||||
call.callElement.getValueArgumentList() != null // Has argument expression
|
||||
)
|
||||
}
|
||||
|
||||
override fun replaceCall(call: CallDescription, editor: Editor) {
|
||||
call.element.replace(JetPsiFactory(call.element).createExpression(
|
||||
lookup(call.functionName)!! + call.element.getReceiverExpression().getText()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
org.jetbrains.kotlin.idea.intentions.TestableReplaceBinaryInfixIntention
|
||||
org.jetbrains.kotlin.idea.intentions.attributeCallReplacements.ReplaceBinaryInfixIntention
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// SHOULD_FAIL_WITH: skipped.defaults
|
||||
// IS_APPLICABLE: false
|
||||
fun test() {
|
||||
class Test{
|
||||
fun plus(a: Int=1, b: Int=2) : Int = 0
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// SHOULD_FAIL_WITH: vararg.not.last
|
||||
// IS_APPLICABLE: false
|
||||
fun test() {
|
||||
class Test{
|
||||
fun plus(vararg b: Int, c: Int = 0): Int = 0
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
org.jetbrains.kotlin.idea.intentions.TestableReplaceContainsIntention
|
||||
org.jetbrains.kotlin.idea.intentions.attributeCallReplacements.ReplaceContainsIntention
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// SHOULD_FAIL_WITH: invalid.arguments
|
||||
// IS_APPLICABLE: false
|
||||
// ERROR: Cannot find a parameter with this name: c
|
||||
fun test() {
|
||||
class Test{
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// SHOULD_FAIL_WITH: duplicate.or.missing.arguments
|
||||
// IS_APPLICABLE: false
|
||||
// ERROR: No value passed for parameter b
|
||||
fun test() {
|
||||
class Test{
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// SHOULD_FAIL_WITH: skipped.defaults
|
||||
// IS_APPLICABLE: false
|
||||
fun test() {
|
||||
class Test{
|
||||
fun contains(a: Int=1, b: Int=2) : Boolean = true
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// SHOULD_FAIL_WITH: vararg.not.last
|
||||
// IS_APPLICABLE: false
|
||||
fun test() {
|
||||
class Test{
|
||||
fun contains(vararg b: Int, c: Int = 0): Boolean = true
|
||||
|
||||
@@ -1 +1 @@
|
||||
org.jetbrains.kotlin.idea.intentions.TestableReplaceGetIntention
|
||||
org.jetbrains.kotlin.idea.intentions.attributeCallReplacements.ReplaceGetIntention
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// SHOULD_FAIL_WITH: duplicate.or.missing.arguments
|
||||
// IS_APPLICABLE: false
|
||||
// ERROR: An argument is already passed for this parameter
|
||||
// ERROR: No value passed for parameter b
|
||||
fun test() {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// SHOULD_FAIL_WITH: invalid.arguments
|
||||
// IS_APPLICABLE: false
|
||||
// ERROR: Cannot find a parameter with this name: c
|
||||
fun test() {
|
||||
class Test{
|
||||
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
// SHOULD_FAIL_WITH: duplicate.or.missing.arguments
|
||||
// ERROR: No value passed for parameter b
|
||||
fun test() {
|
||||
class Test{
|
||||
fun get(a: Int, b: Int) : Int = 0
|
||||
}
|
||||
val test = Test()
|
||||
test.g<caret>et(0)
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// SHOULD_FAIL_WITH: skipped.defaults
|
||||
// IS_APPLICABLE: false
|
||||
fun test() {
|
||||
class Test{
|
||||
fun get(a: Int=1, b: Int=2) : Int = 0
|
||||
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
fun test() {
|
||||
class Test{
|
||||
fun get(a: Int, b: Int, c: Int = 1, d: Int = 1, fn: (i: Int) -> Int) : Int = 0
|
||||
}
|
||||
val test = Test()
|
||||
test.g<caret>et(a=1, c=3, b=2, d=4) { i ->
|
||||
i
|
||||
}
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
fun test() {
|
||||
class Test{
|
||||
fun get(a: Int, b: Int, c: Int = 1, d: Int = 1, fn: (i: Int) -> Int) : Int = 0
|
||||
}
|
||||
val test = Test()
|
||||
test[1, 2, 3, 4, { i ->
|
||||
i
|
||||
}]
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
fun test() {
|
||||
class Test{
|
||||
fun get(a: Int) : Int = 0
|
||||
}
|
||||
val test = Test()
|
||||
test.g<caret>et(a=1)
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
fun test() {
|
||||
class Test{
|
||||
fun get(a: Int) : Int = 0
|
||||
}
|
||||
val test = Test()
|
||||
test[1]
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// SHOULD_FAIL_WITH: vararg.not.last
|
||||
// IS_APPLICABLE: false
|
||||
fun test() {
|
||||
class Test{
|
||||
fun get(a: Int, vararg b: Int, c: Int = 0) : Int = 0
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
fun test() {
|
||||
class Test{
|
||||
fun get(a: Int = 0, b: Int = 1, c: Int = 2, d: Int = 3) : Int = 0
|
||||
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
fun test() {
|
||||
class Test{
|
||||
fun get(a: Int = 0, b: Int = 1, c: Int = 2, d: Int = 3) : Int = 0
|
||||
}
|
||||
val test = Test()
|
||||
test[1, 2, 3]
|
||||
}
|
||||
+1
-1
@@ -1 +1 @@
|
||||
org.jetbrains.kotlin.idea.intentions.TestableReplaceInvokeIntention
|
||||
org.jetbrains.kotlin.idea.intentions.attributeCallReplacements.ReplaceInvokeIntention
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun test() {
|
||||
class Test(val v: Int)
|
||||
|
||||
fun Test.invoke(): Unit = Unit
|
||||
|
||||
Test(1).i<caret>nvoke()
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun test() {
|
||||
class Test(val v: Int)
|
||||
|
||||
fun Test.invoke(): Unit = Unit
|
||||
|
||||
Test(1)<caret>()
|
||||
}
|
||||
+1
-1
@@ -1 +1 @@
|
||||
org.jetbrains.kotlin.idea.intentions.TestableReplaceUnaryPrefixIntention
|
||||
org.jetbrains.kotlin.idea.intentions.attributeCallReplacements.ReplaceUnaryPrefixIntention
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// INTENTION_TEXT: Replace with '-' prefix
|
||||
// INTENTION_TEXT: Replace with '-' operator
|
||||
fun test() {
|
||||
class Test {
|
||||
fun minus(): Test = Test()
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// INTENTION_TEXT: Replace with '-' prefix
|
||||
// INTENTION_TEXT: Replace with '-' operator
|
||||
fun test() {
|
||||
class Test {
|
||||
fun minus(): Test = Test()
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// INTENTION_TEXT: Replace with '!' prefix
|
||||
// INTENTION_TEXT: Replace with '!' operator
|
||||
fun test() {
|
||||
class Test {
|
||||
fun not(): Test = Test()
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// INTENTION_TEXT: Replace with '!' prefix
|
||||
// INTENTION_TEXT: Replace with '!' operator
|
||||
fun test() {
|
||||
class Test {
|
||||
fun not(): Test = Test()
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// INTENTION_TEXT: Replace with '+' prefix
|
||||
// INTENTION_TEXT: Replace with '+' operator
|
||||
fun test() {
|
||||
class Test {
|
||||
fun plus(): Test = Test()
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// INTENTION_TEXT: Replace with '+' prefix
|
||||
// INTENTION_TEXT: Replace with '+' operator
|
||||
fun test() {
|
||||
class Test {
|
||||
fun plus(): Test = Test()
|
||||
|
||||
@@ -174,9 +174,6 @@ public abstract class AbstractIntentionTest extends KotlinCodeInsightTestCase {
|
||||
}
|
||||
assertNull("Expected test to fail.", shouldFailString);
|
||||
}
|
||||
catch (IntentionTestException e) {
|
||||
assertEquals("Failure message mismatch.", shouldFailString, e.getMessage());
|
||||
}
|
||||
catch (BaseRefactoringProcessor.ConflictsInTestsException e) {
|
||||
assertEquals("Failure message mismatch.", shouldFailString, StringUtil.join(e.getMessages(), ", "));
|
||||
}
|
||||
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.intentions
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import org.jetbrains.kotlin.idea.intentions.attributeCallReplacements.*
|
||||
|
||||
trait AttributeCallReplacementIntentionTestMixin : AttributeCallReplacementIntention {
|
||||
protected override fun intentionFailed(editor: Editor, messageId: String, vararg values: Any?) {
|
||||
throw IntentionTestException(messageId)
|
||||
}
|
||||
}
|
||||
|
||||
class TestableReplaceGetIntention() : ReplaceGetIntention(), AttributeCallReplacementIntentionTestMixin
|
||||
class TestableReplaceContainsIntention() : ReplaceContainsIntention(), AttributeCallReplacementIntentionTestMixin
|
||||
class TestableReplaceBinaryInfixIntention() : ReplaceBinaryInfixIntention(), AttributeCallReplacementIntentionTestMixin
|
||||
class TestableReplaceUnaryPrefixIntention() : ReplaceUnaryPrefixIntention(), AttributeCallReplacementIntentionTestMixin
|
||||
class TestableReplaceInvokeIntention() : ReplaceInvokeIntention(), AttributeCallReplacementIntentionTestMixin
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.intentions
|
||||
|
||||
public class IntentionTestException(message: String) : RuntimeException(message)
|
||||
@@ -372,12 +372,6 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("missingArgument.kt")
|
||||
public void testMissingArgument() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/attributeCallReplacements/replaceGetIntention/missingArgument.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("missingDefaultArgument.kt")
|
||||
public void testMissingDefaultArgument() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/attributeCallReplacements/replaceGetIntention/missingDefaultArgument.kt");
|
||||
@@ -390,12 +384,6 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("namedAndFunction.kt")
|
||||
public void testNamedAndFunction() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/attributeCallReplacements/replaceGetIntention/namedAndFunction.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noArgument.kt")
|
||||
public void testNoArgument() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/attributeCallReplacements/replaceGetIntention/noArgument.kt");
|
||||
@@ -414,12 +402,6 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("singleNamedArgument.kt")
|
||||
public void testSingleNamedArgument() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/attributeCallReplacements/replaceGetIntention/singleNamedArgument.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("unacceptableVararg.kt")
|
||||
public void testUnacceptableVararg() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/attributeCallReplacements/replaceGetIntention/unacceptableVararg.kt");
|
||||
@@ -442,6 +424,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/attributeCallReplacements/replaceInvokeIntention"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("expressionReceiver.kt")
|
||||
public void testExpressionReceiver() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/attributeCallReplacements/replaceInvokeIntention/expressionReceiver.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("extensionFunction.kt")
|
||||
public void testExtensionFunction() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/attributeCallReplacements/replaceInvokeIntention/extensionFunction.kt");
|
||||
|
||||
Reference in New Issue
Block a user