Don't invoke formatter while checking availability of intentions
#KT-21632 Fixed
This commit is contained in:
@@ -22,6 +22,7 @@ import com.intellij.psi.SmartPointerManager
|
|||||||
import com.intellij.psi.SmartPsiElementPointer
|
import com.intellij.psi.SmartPsiElementPointer
|
||||||
import com.intellij.psi.codeStyle.CodeStyleManager
|
import com.intellij.psi.codeStyle.CodeStyleManager
|
||||||
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil
|
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil
|
||||||
|
import org.jetbrains.annotations.TestOnly
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.PsiChildRange
|
import org.jetbrains.kotlin.psi.psiUtil.PsiChildRange
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||||
@@ -94,6 +95,9 @@ private val SUPPORTED_ARGUMENT_TYPES = listOf(
|
|||||||
PsiChildRangeArgumentType
|
PsiChildRangeArgumentType
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@TestOnly
|
||||||
|
var CREATEBYPATTERN_MAY_NOT_REFORMAT = false
|
||||||
|
|
||||||
fun <TElement : KtElement> createByPattern(pattern: String, vararg args: Any, reformat: Boolean = true, factory: (String) -> TElement): TElement {
|
fun <TElement : KtElement> createByPattern(pattern: String, vararg args: Any, reformat: Boolean = true, factory: (String) -> TElement): TElement {
|
||||||
val argumentTypes = args.map { arg ->
|
val argumentTypes = args.map { arg ->
|
||||||
SUPPORTED_ARGUMENT_TYPES.firstOrNull { it.klass.isInstance(arg) }
|
SUPPORTED_ARGUMENT_TYPES.firstOrNull { it.klass.isInstance(arg) }
|
||||||
@@ -147,6 +151,9 @@ fun <TElement : KtElement> createByPattern(pattern: String, vararg args: Any, re
|
|||||||
val codeStyleManager = CodeStyleManager.getInstance(project)
|
val codeStyleManager = CodeStyleManager.getInstance(project)
|
||||||
|
|
||||||
if (reformat) {
|
if (reformat) {
|
||||||
|
if (CREATEBYPATTERN_MAY_NOT_REFORMAT) {
|
||||||
|
throw java.lang.IllegalArgumentException("Reformatting is not allowed in the current context; please change the invocation to use reformat=false")
|
||||||
|
}
|
||||||
val stringPlaceholderRanges = allPlaceholders
|
val stringPlaceholderRanges = allPlaceholders
|
||||||
.filter { args[it.key] is String }
|
.filter { args[it.key] is String }
|
||||||
.flatMap { it.value }
|
.flatMap { it.value }
|
||||||
@@ -325,8 +332,8 @@ class BuilderByPattern<TElement> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KtPsiFactory.buildExpression(build: BuilderByPattern<KtExpression>.() -> Unit): KtExpression {
|
fun KtPsiFactory.buildExpression(reformat: Boolean = true, build: BuilderByPattern<KtExpression>.() -> Unit): KtExpression {
|
||||||
return buildByPattern({ pattern, args -> this.createExpressionByPattern(pattern, *args) }, build)
|
return buildByPattern({ pattern, args -> this.createExpressionByPattern(pattern, *args, reformat = reformat) }, build)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KtPsiFactory.buildValueArgumentList(build: BuilderByPattern<KtValueArgumentList>.() -> Unit): KtValueArgumentList {
|
fun KtPsiFactory.buildValueArgumentList(build: BuilderByPattern<KtValueArgumentList>.() -> Unit): KtValueArgumentList {
|
||||||
|
|||||||
+13
-1
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.idea.intentions
|
|||||||
import com.intellij.codeInsight.FileModificationService
|
import com.intellij.codeInsight.FileModificationService
|
||||||
import com.intellij.codeInsight.intention.IntentionAction
|
import com.intellij.codeInsight.intention.IntentionAction
|
||||||
import com.intellij.codeInspection.IntentionWrapper
|
import com.intellij.codeInspection.IntentionWrapper
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
@@ -27,6 +28,7 @@ import com.intellij.psi.PsiElement
|
|||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
import com.intellij.psi.util.PsiTreeUtil
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
|
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
|
||||||
|
import org.jetbrains.kotlin.psi.CREATEBYPATTERN_MAY_NOT_REFORMAT
|
||||||
import org.jetbrains.kotlin.psi.KtBlockExpression
|
import org.jetbrains.kotlin.psi.KtBlockExpression
|
||||||
import org.jetbrains.kotlin.psi.KtElement
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.containsInside
|
import org.jetbrains.kotlin.psi.psiUtil.containsInside
|
||||||
@@ -82,7 +84,17 @@ abstract class SelfTargetingIntention<TElement : PsiElement>(
|
|||||||
protected open fun allowCaretInsideElement(element: PsiElement): Boolean =
|
protected open fun allowCaretInsideElement(element: PsiElement): Boolean =
|
||||||
element !is KtBlockExpression
|
element !is KtBlockExpression
|
||||||
|
|
||||||
final override fun isAvailable(project: Project, editor: Editor, file: PsiFile) = getTarget(editor, file) != null
|
final override fun isAvailable(project: Project, editor: Editor, file: PsiFile): Boolean {
|
||||||
|
if (ApplicationManager.getApplication().isUnitTestMode) {
|
||||||
|
CREATEBYPATTERN_MAY_NOT_REFORMAT = true
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return getTarget(editor, file) != null
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
CREATEBYPATTERN_MAY_NOT_REFORMAT = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var inspection: IntentionBasedInspection<TElement>? = null
|
var inspection: IntentionBasedInspection<TElement>? = null
|
||||||
internal set
|
internal set
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class AddForLoopIndicesIntention : SelfTargetingRangeIntention<KtForExpression>(
|
|||||||
val resolvedCall = loopRange.getResolvedCall(bindingContext)
|
val resolvedCall = loopRange.getResolvedCall(bindingContext)
|
||||||
if (resolvedCall?.resultingDescriptor?.fqNameUnsafe?.asString() in WITH_INDEX_FQ_NAMES) return null // already withIndex() call
|
if (resolvedCall?.resultingDescriptor?.fqNameUnsafe?.asString() in WITH_INDEX_FQ_NAMES) return null // already withIndex() call
|
||||||
|
|
||||||
val potentialExpression = createWithIndexExpression(loopRange)
|
val potentialExpression = createWithIndexExpression(loopRange, reformat = false)
|
||||||
|
|
||||||
val newBindingContext = potentialExpression.analyzeAsReplacement(loopRange, bindingContext)
|
val newBindingContext = potentialExpression.analyzeAsReplacement(loopRange, bindingContext)
|
||||||
val newResolvedCall = potentialExpression.getResolvedCall(newBindingContext) ?: return null
|
val newResolvedCall = potentialExpression.getResolvedCall(newBindingContext) ?: return null
|
||||||
@@ -59,7 +59,7 @@ class AddForLoopIndicesIntention : SelfTargetingRangeIntention<KtForExpression>(
|
|||||||
val loopParameter = element.loopParameter!!
|
val loopParameter = element.loopParameter!!
|
||||||
val psiFactory = KtPsiFactory(element)
|
val psiFactory = KtPsiFactory(element)
|
||||||
|
|
||||||
loopRange.replace(createWithIndexExpression(loopRange))
|
loopRange.replace(createWithIndexExpression(loopRange, reformat = true))
|
||||||
|
|
||||||
var multiParameter = (psiFactory.createExpressionByPattern("for((index, $0) in x){}", loopParameter.text) as KtForExpression).destructuringDeclaration!!
|
var multiParameter = (psiFactory.createExpressionByPattern("for((index, $0) in x){}", loopParameter.text) as KtForExpression).destructuringDeclaration!!
|
||||||
|
|
||||||
@@ -97,7 +97,8 @@ class AddForLoopIndicesIntention : SelfTargetingRangeIntention<KtForExpression>(
|
|||||||
templateBuilder.run(editor, true)
|
templateBuilder.run(editor, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createWithIndexExpression(originalExpression: KtExpression): KtExpression {
|
private fun createWithIndexExpression(originalExpression: KtExpression, reformat: Boolean): KtExpression {
|
||||||
return KtPsiFactory(originalExpression).createExpressionByPattern("$0.$WITH_INDEX_NAME()", originalExpression)
|
return KtPsiFactory(originalExpression).createExpressionByPattern("$0.$WITH_INDEX_NAME()", originalExpression,
|
||||||
|
reformat = reformat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-4
@@ -51,7 +51,10 @@ class RemoveExplicitSuperQualifierIntention : SelfTargetingRangeIntention<KtSupe
|
|||||||
val bindingContext = selector.analyze(BodyResolveMode.PARTIAL)
|
val bindingContext = selector.analyze(BodyResolveMode.PARTIAL)
|
||||||
if (selector.getResolvedCall(bindingContext) == null) return null
|
if (selector.getResolvedCall(bindingContext) == null) return null
|
||||||
|
|
||||||
val newQualifiedExpression = KtPsiFactory(element).createExpressionByPattern("$0.$1", toNonQualified(element), selector) as KtQualifiedExpression
|
val newQualifiedExpression = KtPsiFactory(element).createExpressionByPattern(
|
||||||
|
"$0.$1", toNonQualified(element, reformat = false), selector,
|
||||||
|
reformat = false
|
||||||
|
) as KtQualifiedExpression
|
||||||
val newBindingContext = newQualifiedExpression.analyzeAsReplacement(qualifiedExpression, bindingContext)
|
val newBindingContext = newQualifiedExpression.analyzeAsReplacement(qualifiedExpression, bindingContext)
|
||||||
val newResolvedCall = newQualifiedExpression.selectorExpression.getResolvedCall(newBindingContext) ?: return null
|
val newResolvedCall = newQualifiedExpression.selectorExpression.getResolvedCall(newBindingContext) ?: return null
|
||||||
if (ErrorUtils.isError(newResolvedCall.resultingDescriptor)) return null
|
if (ErrorUtils.isError(newResolvedCall.resultingDescriptor)) return null
|
||||||
@@ -60,14 +63,14 @@ class RemoveExplicitSuperQualifierIntention : SelfTargetingRangeIntention<KtSupe
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun applyTo(element: KtSuperExpression, editor: Editor?) {
|
override fun applyTo(element: KtSuperExpression, editor: Editor?) {
|
||||||
element.replace(toNonQualified(element))
|
element.replace(toNonQualified(element, reformat = true))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun toNonQualified(superExpression: KtSuperExpression): KtSuperExpression {
|
private fun toNonQualified(superExpression: KtSuperExpression, reformat: Boolean): KtSuperExpression {
|
||||||
val factory = KtPsiFactory(superExpression)
|
val factory = KtPsiFactory(superExpression)
|
||||||
val labelName = superExpression.getLabelNameAsName()
|
val labelName = superExpression.getLabelNameAsName()
|
||||||
return (if (labelName != null)
|
return (if (labelName != null)
|
||||||
factory.createExpressionByPattern("super@$0", labelName)
|
factory.createExpressionByPattern("super@$0", labelName, reformat = reformat)
|
||||||
else
|
else
|
||||||
factory.createExpression("super")) as KtSuperExpression
|
factory.createExpression("super")) as KtSuperExpression
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,14 +119,14 @@ class UsePropertyAccessSyntaxIntention : SelfTargetingOffsetIndependentIntention
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun applyTo(element: KtCallExpression, editor: Editor?) {
|
override fun applyTo(element: KtCallExpression, editor: Editor?) {
|
||||||
applyTo(element, detectPropertyNameToUse(element)!!)
|
applyTo(element, detectPropertyNameToUse(element)!!, reformat = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun applyTo(element: KtCallExpression, propertyName: Name): KtExpression {
|
fun applyTo(element: KtCallExpression, propertyName: Name, reformat: Boolean): KtExpression {
|
||||||
val arguments = element.valueArguments
|
val arguments = element.valueArguments
|
||||||
return when (arguments.size) {
|
return when (arguments.size) {
|
||||||
0 -> replaceWithPropertyGet(element, propertyName)
|
0 -> replaceWithPropertyGet(element, propertyName)
|
||||||
1 -> replaceWithPropertySet(element, propertyName)
|
1 -> replaceWithPropertySet(element, propertyName, reformat)
|
||||||
else -> error("More than one argument in call to accessor")
|
else -> error("More than one argument in call to accessor")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,7 +168,7 @@ class UsePropertyAccessSyntaxIntention : SelfTargetingOffsetIndependentIntention
|
|||||||
if (isSetUsage && property.type != function.valueParameters.single().type) {
|
if (isSetUsage && property.type != function.valueParameters.single().type) {
|
||||||
val qualifiedExpressionCopy = qualifiedExpression.copied()
|
val qualifiedExpressionCopy = qualifiedExpression.copied()
|
||||||
val callExpressionCopy = ((qualifiedExpressionCopy as? KtQualifiedExpression)?.selectorExpression ?: qualifiedExpressionCopy) as KtCallExpression
|
val callExpressionCopy = ((qualifiedExpressionCopy as? KtQualifiedExpression)?.selectorExpression ?: qualifiedExpressionCopy) as KtCallExpression
|
||||||
val newExpression = applyTo(callExpressionCopy, property.name)
|
val newExpression = applyTo(callExpressionCopy, property.name, reformat = false)
|
||||||
val bindingTrace = DelegatingBindingTrace(bindingContext, "Temporary trace")
|
val bindingTrace = DelegatingBindingTrace(bindingContext, "Temporary trace")
|
||||||
val newBindingContext = newExpression.analyzeInContext(
|
val newBindingContext = newExpression.analyzeInContext(
|
||||||
resolutionScope,
|
resolutionScope,
|
||||||
@@ -195,7 +195,7 @@ class UsePropertyAccessSyntaxIntention : SelfTargetingOffsetIndependentIntention
|
|||||||
): Boolean {
|
): Boolean {
|
||||||
val project = resolvedCall.call.callElement.project
|
val project = resolvedCall.call.callElement.project
|
||||||
val newCall = object : DelegatingCall(resolvedCall.call) {
|
val newCall = object : DelegatingCall(resolvedCall.call) {
|
||||||
private val newCallee = KtPsiFactory(project).createExpressionByPattern("$0", property.name)
|
private val newCallee = KtPsiFactory(project).createExpressionByPattern("$0", property.name, reformat = false)
|
||||||
|
|
||||||
override fun getCalleeExpression() = newCallee
|
override fun getCalleeExpression() = newCallee
|
||||||
override fun getValueArgumentList(): KtValueArgumentList? = null
|
override fun getValueArgumentList(): KtValueArgumentList? = null
|
||||||
@@ -227,7 +227,7 @@ class UsePropertyAccessSyntaxIntention : SelfTargetingOffsetIndependentIntention
|
|||||||
return callExpression.replaced(newExpression)
|
return callExpression.replaced(newExpression)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun replaceWithPropertySet(callExpression: KtCallExpression, propertyName: Name): KtExpression {
|
private fun replaceWithPropertySet(callExpression: KtCallExpression, propertyName: Name, reformat: Boolean): KtExpression {
|
||||||
val call = callExpression.getQualifiedExpressionForSelector() ?: callExpression
|
val call = callExpression.getQualifiedExpressionForSelector() ?: callExpression
|
||||||
val callParent = call.parent
|
val callParent = call.parent
|
||||||
var callToConvert = callExpression
|
var callToConvert = callExpression
|
||||||
@@ -251,7 +251,8 @@ class UsePropertyAccessSyntaxIntention : SelfTargetingOffsetIndependentIntention
|
|||||||
pattern,
|
pattern,
|
||||||
qualifiedExpression.receiverExpression,
|
qualifiedExpression.receiverExpression,
|
||||||
propertyName,
|
propertyName,
|
||||||
argument.getArgumentExpression()!!
|
argument.getArgumentExpression()!!,
|
||||||
|
reformat = reformat
|
||||||
)
|
)
|
||||||
return qualifiedExpression.replaced(newExpression)
|
return qualifiedExpression.replaced(newExpression)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,10 +118,10 @@ fun KtQualifiedExpression.isReceiverExpressionWithValue(): Boolean {
|
|||||||
return analyze().getType(receiver) != null
|
return analyze().getType(receiver) != null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KtExpression.negate(): KtExpression {
|
fun KtExpression.negate(reformat: Boolean = true): KtExpression {
|
||||||
val specialNegation = specialNegation()
|
val specialNegation = specialNegation(reformat)
|
||||||
if (specialNegation != null) return specialNegation
|
if (specialNegation != null) return specialNegation
|
||||||
return KtPsiFactory(this).createExpressionByPattern("!$0", this)
|
return KtPsiFactory(this).createExpressionByPattern("!$0", this, reformat = reformat)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KtExpression.resultingWhens(): List<KtWhenExpression> = when (this) {
|
fun KtExpression.resultingWhens(): List<KtWhenExpression> = when (this) {
|
||||||
@@ -142,7 +142,7 @@ fun KtExpression?.hasResultingIfWithoutElse(): Boolean = when (this) {
|
|||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KtExpression.specialNegation(): KtExpression? {
|
private fun KtExpression.specialNegation(reformat: Boolean): KtExpression? {
|
||||||
val factory = KtPsiFactory(this)
|
val factory = KtPsiFactory(this)
|
||||||
when (this) {
|
when (this) {
|
||||||
is KtPrefixExpression -> {
|
is KtPrefixExpression -> {
|
||||||
@@ -163,14 +163,18 @@ private fun KtExpression.specialNegation(): KtExpression? {
|
|||||||
if (operator !in NEGATABLE_OPERATORS) return null
|
if (operator !in NEGATABLE_OPERATORS) return null
|
||||||
val left = left ?: return null
|
val left = left ?: return null
|
||||||
val right = right ?: return null
|
val right = right ?: return null
|
||||||
return factory.createExpressionByPattern("$0 $1 $2", left, getNegatedOperatorText(operator), right)
|
return factory.createExpressionByPattern(
|
||||||
|
"$0 $1 $2", left, getNegatedOperatorText(operator), right,
|
||||||
|
reformat = reformat
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
is KtIsExpression -> {
|
is KtIsExpression -> {
|
||||||
return factory.createExpressionByPattern("$0 $1 $2",
|
return factory.createExpressionByPattern("$0 $1 $2",
|
||||||
leftHandSide,
|
leftHandSide,
|
||||||
if (isNegated) "is" else "!is",
|
if (isNegated) "is" else "!is",
|
||||||
typeReference ?: return null)
|
typeReference ?: return null,
|
||||||
|
reformat = reformat)
|
||||||
}
|
}
|
||||||
|
|
||||||
is KtConstantExpression -> {
|
is KtConstantExpression -> {
|
||||||
|
|||||||
+2
-2
@@ -41,7 +41,7 @@ abstract class AbstractLoopToCallChainIntention(private val lazy: Boolean, text:
|
|||||||
text
|
text
|
||||||
) {
|
) {
|
||||||
override fun applicabilityRange(element: KtForExpression): TextRange? {
|
override fun applicabilityRange(element: KtForExpression): TextRange? {
|
||||||
val match = match(element, lazy)
|
val match = match(element, lazy, false)
|
||||||
text = if (match != null) "Replace with '${match.transformationMatch.buildPresentation()}'" else defaultText
|
text = if (match != null) "Replace with '${match.transformationMatch.buildPresentation()}'" else defaultText
|
||||||
return if (match != null) element.forKeyword.textRange else null
|
return if (match != null) element.forKeyword.textRange else null
|
||||||
}
|
}
|
||||||
@@ -68,7 +68,7 @@ abstract class AbstractLoopToCallChainIntention(private val lazy: Boolean, text:
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun applyTo(element: KtForExpression, editor: Editor?) {
|
override fun applyTo(element: KtForExpression, editor: Editor?) {
|
||||||
val match = match(element, lazy)!!
|
val match = match(element, lazy, true)!!
|
||||||
val result = convertLoop(element, match)
|
val result = convertLoop(element, match)
|
||||||
|
|
||||||
val offset = when (result) {
|
val offset = when (result) {
|
||||||
|
|||||||
+3
-3
@@ -32,11 +32,11 @@ class UseWithIndexIntention : SelfTargetingRangeIntention<KtForExpression>(
|
|||||||
"Use withIndex() instead of manual index increment"
|
"Use withIndex() instead of manual index increment"
|
||||||
) {
|
) {
|
||||||
override fun applicabilityRange(element: KtForExpression): TextRange? {
|
override fun applicabilityRange(element: KtForExpression): TextRange? {
|
||||||
return if (matchIndexToIntroduce(element) != null) element.forKeyword.textRange else null
|
return if (matchIndexToIntroduce(element, reformat = false) != null) element.forKeyword.textRange else null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun applyTo(element: KtForExpression, editor: Editor?) {
|
override fun applyTo(element: KtForExpression, editor: Editor?) {
|
||||||
val (indexVariable, initializationStatement, incrementExpression) = matchIndexToIntroduce(element)!!
|
val (indexVariable, initializationStatement, incrementExpression) = matchIndexToIntroduce(element, reformat = true)!!
|
||||||
|
|
||||||
val factory = KtPsiFactory(element)
|
val factory = KtPsiFactory(element)
|
||||||
val loopRange = element.loopRange!!
|
val loopRange = element.loopRange!!
|
||||||
@@ -53,7 +53,7 @@ class UseWithIndexIntention : SelfTargetingRangeIntention<KtForExpression>(
|
|||||||
incrementExpression.delete()
|
incrementExpression.delete()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
removePlusPlus(incrementExpression)
|
removePlusPlus(incrementExpression, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -76,7 +76,7 @@ abstract class AssignToVariableResultTransformation(
|
|||||||
copy
|
copy
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
psiFactory.createExpressionByPattern("$0 = $1", initialization.variable.nameAsSafeName, resultCallChain)
|
psiFactory.createExpressionByPattern("$0 = $1", initialization.variable.nameAsSafeName, resultCallChain, reformat = false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.psi.psiUtil.siblings
|
|||||||
*/
|
*/
|
||||||
interface ChainedCallGenerator {
|
interface ChainedCallGenerator {
|
||||||
val receiver: KtExpression
|
val receiver: KtExpression
|
||||||
|
val reformat: Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param pattern pattern string for generating the part of the call to the right from the dot
|
* @param pattern pattern string for generating the part of the call to the right from the dot
|
||||||
@@ -54,7 +55,7 @@ interface Transformation {
|
|||||||
presentation
|
presentation
|
||||||
}
|
}
|
||||||
|
|
||||||
fun mergeWithPrevious(previousTransformation: SequenceTransformation): Transformation?
|
fun mergeWithPrevious(previousTransformation: SequenceTransformation, reformat: Boolean): Transformation?
|
||||||
|
|
||||||
fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression
|
fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression
|
||||||
|
|
||||||
@@ -66,7 +67,7 @@ interface Transformation {
|
|||||||
* Represents a transformation of input sequence into another sequence
|
* Represents a transformation of input sequence into another sequence
|
||||||
*/
|
*/
|
||||||
interface SequenceTransformation : Transformation {
|
interface SequenceTransformation : Transformation {
|
||||||
override fun mergeWithPrevious(previousTransformation: SequenceTransformation): SequenceTransformation? = null
|
override fun mergeWithPrevious(previousTransformation: SequenceTransformation, reformat: Boolean): SequenceTransformation? = null
|
||||||
|
|
||||||
val affectsIndex: Boolean
|
val affectsIndex: Boolean
|
||||||
}
|
}
|
||||||
@@ -75,7 +76,7 @@ interface SequenceTransformation : Transformation {
|
|||||||
* Represents a final transformation of sequence which produces the result of the whole loop (for example, assigning a found value into a variable).
|
* Represents a final transformation of sequence which produces the result of the whole loop (for example, assigning a found value into a variable).
|
||||||
*/
|
*/
|
||||||
interface ResultTransformation : Transformation {
|
interface ResultTransformation : Transformation {
|
||||||
override fun mergeWithPrevious(previousTransformation: SequenceTransformation): ResultTransformation? = null
|
override fun mergeWithPrevious(previousTransformation: SequenceTransformation, reformat: Boolean): ResultTransformation? = null
|
||||||
|
|
||||||
val commentSavingRange: PsiChildRange
|
val commentSavingRange: PsiChildRange
|
||||||
|
|
||||||
@@ -105,6 +106,7 @@ data class MatchingState(
|
|||||||
val indexVariable: KtCallableDeclaration?,
|
val indexVariable: KtCallableDeclaration?,
|
||||||
val lazySequence: Boolean,
|
val lazySequence: Boolean,
|
||||||
val pseudocodeProvider: () -> Pseudocode,
|
val pseudocodeProvider: () -> Pseudocode,
|
||||||
|
val reformat: Boolean,
|
||||||
val initializationStatementsToDelete: Collection<KtExpression> = emptyList(),
|
val initializationStatementsToDelete: Collection<KtExpression> = emptyList(),
|
||||||
val previousTransformations: MutableList<SequenceTransformation> = arrayListOf(),
|
val previousTransformations: MutableList<SequenceTransformation> = arrayListOf(),
|
||||||
val incrementExpressions: Collection<KtUnaryExpression> = emptyList()
|
val incrementExpressions: Collection<KtUnaryExpression> = emptyList()
|
||||||
|
|||||||
@@ -59,10 +59,10 @@ data class MatchResult(
|
|||||||
)
|
)
|
||||||
|
|
||||||
//TODO: loop which is already over Sequence
|
//TODO: loop which is already over Sequence
|
||||||
fun match(loop: KtForExpression, useLazySequence: Boolean): MatchResult? {
|
fun match(loop: KtForExpression, useLazySequence: Boolean, reformat: Boolean): MatchResult? {
|
||||||
val (inputVariable, indexVariable, sequenceExpression) = extractLoopData(loop) ?: return null
|
val (inputVariable, indexVariable, sequenceExpression) = extractLoopData(loop) ?: return null
|
||||||
|
|
||||||
var state = createInitialMatchingState(loop, inputVariable, indexVariable, useLazySequence) ?: return null
|
var state = createInitialMatchingState(loop, inputVariable, indexVariable, useLazySequence, reformat) ?: return null
|
||||||
|
|
||||||
// used just as optimization to avoid unnecessary checks
|
// used just as optimization to avoid unnecessary checks
|
||||||
val loopContainsEmbeddedBreakOrContinue = loop.containsEmbeddedBreakOrContinue()
|
val loopContainsEmbeddedBreakOrContinue = loop.containsEmbeddedBreakOrContinue()
|
||||||
@@ -123,7 +123,7 @@ fun match(loop: KtForExpression, useLazySequence: Boolean): MatchResult? {
|
|||||||
state.previousTransformations += match.sequenceTransformations
|
state.previousTransformations += match.sequenceTransformations
|
||||||
|
|
||||||
var result = TransformationMatch.Result(match.resultTransformation, state.previousTransformations)
|
var result = TransformationMatch.Result(match.resultTransformation, state.previousTransformations)
|
||||||
result = mergeTransformations(result)
|
result = mergeTransformations(result, reformat)
|
||||||
|
|
||||||
if (useLazySequence) {
|
if (useLazySequence) {
|
||||||
val sequenceTransformations = result.sequenceTransformations
|
val sequenceTransformations = result.sequenceTransformations
|
||||||
@@ -157,7 +157,7 @@ fun convertLoop(loop: KtForExpression, matchResult: MatchResult): KtExpression {
|
|||||||
|
|
||||||
matchResult.initializationStatementsToDelete.forEach { commentSavingRangeHolder.add(it) }
|
matchResult.initializationStatementsToDelete.forEach { commentSavingRangeHolder.add(it) }
|
||||||
|
|
||||||
val callChain = matchResult.generateCallChain(loop)
|
val callChain = matchResult.generateCallChain(loop, true)
|
||||||
|
|
||||||
commentSavingRangeHolder.remove(loop.unwrapIfLabeled()) // loop will be deleted in all cases
|
commentSavingRangeHolder.remove(loop.unwrapIfLabeled()) // loop will be deleted in all cases
|
||||||
val result = resultTransformation.convertLoop(callChain, commentSavingRangeHolder)
|
val result = resultTransformation.convertLoop(callChain, commentSavingRangeHolder)
|
||||||
@@ -204,7 +204,8 @@ private fun createInitialMatchingState(
|
|||||||
loop: KtForExpression,
|
loop: KtForExpression,
|
||||||
inputVariable: KtCallableDeclaration,
|
inputVariable: KtCallableDeclaration,
|
||||||
indexVariable: KtCallableDeclaration?,
|
indexVariable: KtCallableDeclaration?,
|
||||||
useLazySequence: Boolean
|
useLazySequence: Boolean,
|
||||||
|
reformat: Boolean
|
||||||
): MatchingState? {
|
): MatchingState? {
|
||||||
|
|
||||||
val pseudocodeProvider: () -> Pseudocode = object : () -> Pseudocode {
|
val pseudocodeProvider: () -> Pseudocode = object : () -> Pseudocode {
|
||||||
@@ -224,7 +225,8 @@ private fun createInitialMatchingState(
|
|||||||
inputVariable = inputVariable,
|
inputVariable = inputVariable,
|
||||||
indexVariable = indexVariable,
|
indexVariable = indexVariable,
|
||||||
lazySequence = useLazySequence,
|
lazySequence = useLazySequence,
|
||||||
pseudocodeProvider = pseudocodeProvider
|
pseudocodeProvider = pseudocodeProvider,
|
||||||
|
reformat = reformat
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,7 +274,7 @@ private fun checkSmartCastsPreserved(loop: KtForExpression, matchResult: MatchRe
|
|||||||
|
|
||||||
if (smartCastCount == 0) return true // optimization
|
if (smartCastCount == 0) return true // optimization
|
||||||
|
|
||||||
val callChain = matchResult.generateCallChain(loop)
|
val callChain = matchResult.generateCallChain(loop, false)
|
||||||
|
|
||||||
val newBindingContext = callChain.analyzeAsReplacement(loop, bindingContext)
|
val newBindingContext = callChain.analyzeAsReplacement(loop, bindingContext)
|
||||||
|
|
||||||
@@ -312,12 +314,12 @@ private fun checkSmartCastsPreserved(loop: KtForExpression, matchResult: MatchRe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MatchResult.generateCallChain(loop: KtForExpression): KtExpression {
|
private fun MatchResult.generateCallChain(loop: KtForExpression, reformat: Boolean): KtExpression {
|
||||||
var sequenceTransformations = transformationMatch.sequenceTransformations
|
var sequenceTransformations = transformationMatch.sequenceTransformations
|
||||||
var resultTransformation = transformationMatch.resultTransformation
|
var resultTransformation = transformationMatch.resultTransformation
|
||||||
while(true) {
|
while(true) {
|
||||||
val last = sequenceTransformations.lastOrNull() ?: break
|
val last = sequenceTransformations.lastOrNull() ?: break
|
||||||
resultTransformation = resultTransformation.mergeWithPrevious(last) ?: break
|
resultTransformation = resultTransformation.mergeWithPrevious(last, reformat) ?: break
|
||||||
sequenceTransformations = sequenceTransformations.dropLast(1)
|
sequenceTransformations = sequenceTransformations.dropLast(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,10 +333,13 @@ private fun MatchResult.generateCallChain(loop: KtForExpression): KtExpression {
|
|||||||
override val receiver: KtExpression
|
override val receiver: KtExpression
|
||||||
get() = callChain
|
get() = callChain
|
||||||
|
|
||||||
|
override val reformat: Boolean
|
||||||
|
get() = reformat
|
||||||
|
|
||||||
override fun generate(pattern: String, vararg args: Any, receiver: KtExpression, safeCall: Boolean): KtExpression {
|
override fun generate(pattern: String, vararg args: Any, receiver: KtExpression, safeCall: Boolean): KtExpression {
|
||||||
val dot = if (safeCall) "?." else "."
|
val dot = if (safeCall) "?." else "."
|
||||||
val newPattern = "$" + args.size + lineBreak + dot + pattern
|
val newPattern = "$" + args.size + lineBreak + dot + pattern
|
||||||
return psiFactory.createExpressionByPattern(newPattern, *args, receiver)
|
return psiFactory.createExpressionByPattern(newPattern, *args, receiver, reformat = reformat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,7 +351,7 @@ private fun MatchResult.generateCallChain(loop: KtForExpression): KtExpression {
|
|||||||
return callChain
|
return callChain
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun mergeTransformations(match: TransformationMatch.Result): TransformationMatch.Result {
|
private fun mergeTransformations(match: TransformationMatch.Result, reformat: Boolean): TransformationMatch.Result {
|
||||||
val transformations = (match.sequenceTransformations + match.resultTransformation).toMutableList()
|
val transformations = (match.sequenceTransformations + match.resultTransformation).toMutableList()
|
||||||
|
|
||||||
var anyChange: Boolean
|
var anyChange: Boolean
|
||||||
@@ -355,7 +360,7 @@ private fun mergeTransformations(match: TransformationMatch.Result): Transformat
|
|||||||
for (index in 0..transformations.lastIndex - 1) {
|
for (index in 0..transformations.lastIndex - 1) {
|
||||||
val transformation = transformations[index] as SequenceTransformation
|
val transformation = transformations[index] as SequenceTransformation
|
||||||
val next = transformations[index + 1]
|
val next = transformations[index + 1]
|
||||||
val merged = next.mergeWithPrevious(transformation) ?: continue
|
val merged = next.mergeWithPrevious(transformation, reformat) ?: continue
|
||||||
transformations[index] = merged
|
transformations[index] = merged
|
||||||
transformations.removeAt(index + 1)
|
transformations.removeAt(index + 1)
|
||||||
anyChange = true
|
anyChange = true
|
||||||
@@ -373,11 +378,11 @@ data class IntroduceIndexData(
|
|||||||
val incrementExpression: KtUnaryExpression
|
val incrementExpression: KtUnaryExpression
|
||||||
)
|
)
|
||||||
|
|
||||||
fun matchIndexToIntroduce(loop: KtForExpression): IntroduceIndexData? {
|
fun matchIndexToIntroduce(loop: KtForExpression, reformat: Boolean): IntroduceIndexData? {
|
||||||
val (inputVariable, indexVariable) = extractLoopData(loop) ?: return null
|
val (inputVariable, indexVariable) = extractLoopData(loop) ?: return null
|
||||||
if (indexVariable != null) return null // loop is already with "withIndex"
|
if (indexVariable != null) return null // loop is already with "withIndex"
|
||||||
|
|
||||||
val state = createInitialMatchingState(loop, inputVariable, indexVariable, useLazySequence = false)?.unwrapBlock() ?: return null
|
val state = createInitialMatchingState(loop, inputVariable, indexVariable, useLazySequence = false, reformat = reformat)?.unwrapBlock() ?: return null
|
||||||
|
|
||||||
val match = IntroduceIndexMatcher.match(state) ?: return null
|
val match = IntroduceIndexMatcher.match(state) ?: return null
|
||||||
assert(match.sequenceTransformations.isEmpty())
|
assert(match.sequenceTransformations.isEmpty())
|
||||||
|
|||||||
+11
-7
@@ -34,7 +34,7 @@ class AddToCollectionTransformation(
|
|||||||
private val targetCollection: KtExpression
|
private val targetCollection: KtExpression
|
||||||
) : ReplaceLoopResultTransformation(loop) {
|
) : ReplaceLoopResultTransformation(loop) {
|
||||||
|
|
||||||
override fun mergeWithPrevious(previousTransformation: SequenceTransformation): ResultTransformation? {
|
override fun mergeWithPrevious(previousTransformation: SequenceTransformation, reformat: Boolean): ResultTransformation? {
|
||||||
return when (previousTransformation) {
|
return when (previousTransformation) {
|
||||||
is FilterTransformation -> {
|
is FilterTransformation -> {
|
||||||
FilterToTransformation.create(
|
FilterToTransformation.create(
|
||||||
@@ -72,7 +72,10 @@ class AddToCollectionTransformation(
|
|||||||
get() = 0
|
get() = 0
|
||||||
|
|
||||||
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
return KtPsiFactory(loop).createExpressionByPattern("$0 += $1", targetCollection, chainedCallGenerator.receiver)
|
return KtPsiFactory(loop).createExpressionByPattern(
|
||||||
|
"$0 += $1", targetCollection, chainedCallGenerator.receiver,
|
||||||
|
reformat = chainedCallGenerator.reformat
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -219,10 +222,11 @@ class FilterToTransformation private constructor(
|
|||||||
get() = "$functionName(){}"
|
get() = "$functionName(){}"
|
||||||
|
|
||||||
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
|
val reformat = chainedCallGenerator.reformat
|
||||||
val lambda = if (indexVariable != null)
|
val lambda = if (indexVariable != null)
|
||||||
generateLambda(inputVariable, indexVariable, effectiveCondition.asExpression())
|
generateLambda(inputVariable, indexVariable, effectiveCondition.asExpression(reformat), reformat)
|
||||||
else
|
else
|
||||||
generateLambda(inputVariable, if (isFilterNot) effectiveCondition.asNegatedExpression() else effectiveCondition.asExpression())
|
generateLambda(inputVariable, if (isFilterNot) effectiveCondition.asNegatedExpression(reformat) else effectiveCondition.asExpression(reformat), reformat)
|
||||||
return chainedCallGenerator.generate("$functionName($0) $1:'{}'", targetCollection, lambda)
|
return chainedCallGenerator.generate("$functionName($0) $1:'{}'", targetCollection, lambda)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,7 +298,7 @@ class MapToTransformation private constructor(
|
|||||||
get() = "$functionName(){}"
|
get() = "$functionName(){}"
|
||||||
|
|
||||||
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
val lambda = generateLambda(inputVariable, indexVariable, mapping)
|
val lambda = generateLambda(inputVariable, indexVariable, mapping, chainedCallGenerator.reformat)
|
||||||
return chainedCallGenerator.generate("$functionName($0) $1:'{}'", targetCollection, lambda)
|
return chainedCallGenerator.generate("$functionName($0) $1:'{}'", targetCollection, lambda)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,7 +334,7 @@ class FlatMapToTransformation private constructor(
|
|||||||
get() = "flatMapTo(){}"
|
get() = "flatMapTo(){}"
|
||||||
|
|
||||||
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
val lambda = generateLambda(inputVariable, transform)
|
val lambda = generateLambda(inputVariable, transform, chainedCallGenerator.reformat)
|
||||||
return chainedCallGenerator.generate("flatMapTo($0) $1:'{}'", targetCollection, lambda)
|
return chainedCallGenerator.generate("flatMapTo($0) $1:'{}'", targetCollection, lambda)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,7 +371,7 @@ class AssignToListTransformation(
|
|||||||
override val presentation: String
|
override val presentation: String
|
||||||
get() = "toList()"
|
get() = "toList()"
|
||||||
|
|
||||||
override fun mergeWithPrevious(previousTransformation: SequenceTransformation): ResultTransformation? {
|
override fun mergeWithPrevious(previousTransformation: SequenceTransformation, reformat: Boolean): ResultTransformation? {
|
||||||
if (lazySequence) return null // toList() is necessary if the result is Sequence
|
if (lazySequence) return null // toList() is necessary if the result is Sequence
|
||||||
//TODO: can be any SequenceTransformation's that return not List<T>?
|
//TODO: can be any SequenceTransformation's that return not List<T>?
|
||||||
return AssignSequenceResultTransformation(previousTransformation, initialization)
|
return AssignSequenceResultTransformation(previousTransformation, initialization)
|
||||||
|
|||||||
+7
-5
@@ -30,13 +30,14 @@ class CountTransformation(
|
|||||||
private val filter: KtExpression?
|
private val filter: KtExpression?
|
||||||
) : AssignToVariableResultTransformation(loop, initialization) {
|
) : AssignToVariableResultTransformation(loop, initialization) {
|
||||||
|
|
||||||
override fun mergeWithPrevious(previousTransformation: SequenceTransformation): ResultTransformation? {
|
override fun mergeWithPrevious(previousTransformation: SequenceTransformation, reformat: Boolean): ResultTransformation? {
|
||||||
if (previousTransformation !is FilterTransformationBase) return null
|
if (previousTransformation !is FilterTransformationBase) return null
|
||||||
if (previousTransformation.indexVariable != null) return null
|
if (previousTransformation.indexVariable != null) return null
|
||||||
val newFilter = if (filter == null)
|
val newFilter = if (filter == null)
|
||||||
previousTransformation.effectiveCondition.asExpression()
|
previousTransformation.effectiveCondition.asExpression(reformat)
|
||||||
else
|
else
|
||||||
KtPsiFactory(filter).createExpressionByPattern("$0 && $1", previousTransformation.effectiveCondition.asExpression(), filter)
|
KtPsiFactory(filter).createExpressionByPattern("$0 && $1", previousTransformation.effectiveCondition.asExpression(reformat), filter,
|
||||||
|
reformat = reformat)
|
||||||
return CountTransformation(loop, previousTransformation.inputVariable, initialization, newFilter)
|
return CountTransformation(loop, previousTransformation.inputVariable, initialization, newFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,8 +45,9 @@ class CountTransformation(
|
|||||||
get() = "count" + (if (filter != null) "{}" else "()")
|
get() = "count" + (if (filter != null) "{}" else "()")
|
||||||
|
|
||||||
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
|
val reformat = chainedCallGenerator.reformat
|
||||||
val call = if (filter != null) {
|
val call = if (filter != null) {
|
||||||
val lambda = generateLambda(inputVariable, filter)
|
val lambda = generateLambda(inputVariable, filter, reformat)
|
||||||
chainedCallGenerator.generate("count $0:'{}'", lambda)
|
chainedCallGenerator.generate("count $0:'{}'", lambda)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -56,7 +58,7 @@ class CountTransformation(
|
|||||||
call
|
call
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
KtPsiFactory(call).createExpressionByPattern("$0 + $1", initialization.initializer, call)
|
KtPsiFactory(call).createExpressionByPattern("$0 + $1", initialization.initializer, call, reformat = reformat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+28
-18
@@ -91,7 +91,8 @@ object FindTransformationMatcher : TransformationMatcher {
|
|||||||
val generator = buildFindOperationGenerator(state.outerLoop, state.inputVariable, state.indexVariable, filterTransformation,
|
val generator = buildFindOperationGenerator(state.outerLoop, state.inputVariable, state.indexVariable, filterTransformation,
|
||||||
valueIfFound = right,
|
valueIfFound = right,
|
||||||
valueIfNotFound = initialization.initializer,
|
valueIfNotFound = initialization.initializer,
|
||||||
findFirst = findFirst)
|
findFirst = findFirst,
|
||||||
|
reformat = state.reformat)
|
||||||
?: return null
|
?: return null
|
||||||
|
|
||||||
val transformation = FindAndAssignTransformation(state.outerLoop, generator, initialization)
|
val transformation = FindAndAssignTransformation(state.outerLoop, generator, initialization)
|
||||||
@@ -110,7 +111,8 @@ object FindTransformationMatcher : TransformationMatcher {
|
|||||||
filterTransformation,
|
filterTransformation,
|
||||||
valueIfFound = returnValueInLoop,
|
valueIfFound = returnValueInLoop,
|
||||||
valueIfNotFound = returnValueAfterLoop,
|
valueIfNotFound = returnValueAfterLoop,
|
||||||
findFirst = true)
|
findFirst = true,
|
||||||
|
reformat = state.reformat)
|
||||||
?: return null
|
?: return null
|
||||||
|
|
||||||
val transformation = FindAndReturnTransformation(state.outerLoop, generator, returnAfterLoop)
|
val transformation = FindAndReturnTransformation(state.outerLoop, generator, returnAfterLoop)
|
||||||
@@ -136,7 +138,7 @@ object FindTransformationMatcher : TransformationMatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun generateExpressionToReplaceLoopAndCheckErrors(resultCallChain: KtExpression): KtExpression {
|
override fun generateExpressionToReplaceLoopAndCheckErrors(resultCallChain: KtExpression): KtExpression {
|
||||||
return KtPsiFactory(resultCallChain).createExpressionByPattern("return $0", resultCallChain)
|
return KtPsiFactory(resultCallChain).createExpressionByPattern("return $0", resultCallChain, reformat = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun convertLoop(resultCallChain: KtExpression, commentSavingRangeHolder: CommentSavingRangeHolder): KtExpression {
|
override fun convertLoop(resultCallChain: KtExpression, commentSavingRangeHolder: CommentSavingRangeHolder): KtExpression {
|
||||||
@@ -203,7 +205,7 @@ object FindTransformationMatcher : TransformationMatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val lambda = generateLambda(inputVariable, filter)
|
val lambda = generateLambda(inputVariable, filter, chainedCallGenerator.reformat)
|
||||||
if (argument != null) {
|
if (argument != null) {
|
||||||
chainedCallGenerator.generate("$stdlibFunName($0) $1:'{}'", argument, lambda)
|
chainedCallGenerator.generate("$stdlibFunName($0) $1:'{}'", argument, lambda)
|
||||||
}
|
}
|
||||||
@@ -220,7 +222,8 @@ object FindTransformationMatcher : TransformationMatcher {
|
|||||||
filterTransformation: FilterTransformationBase?,
|
filterTransformation: FilterTransformationBase?,
|
||||||
valueIfFound: KtExpression,
|
valueIfFound: KtExpression,
|
||||||
valueIfNotFound: KtExpression,
|
valueIfNotFound: KtExpression,
|
||||||
findFirst: Boolean
|
findFirst: Boolean,
|
||||||
|
reformat: Boolean
|
||||||
): FindOperationGenerator? {
|
): FindOperationGenerator? {
|
||||||
assert(valueIfFound.isPhysical)
|
assert(valueIfFound.isPhysical)
|
||||||
assert(valueIfNotFound.isPhysical)
|
assert(valueIfNotFound.isPhysical)
|
||||||
@@ -233,7 +236,7 @@ object FindTransformationMatcher : TransformationMatcher {
|
|||||||
|
|
||||||
//TODO: what if value when not found is not "-1"?
|
//TODO: what if value when not found is not "-1"?
|
||||||
if (valueIfFound.isVariableReference(indexVariable) && valueIfNotFound.text == "-1") {
|
if (valueIfFound.isVariableReference(indexVariable) && valueIfNotFound.text == "-1") {
|
||||||
val filterExpression = filterCondition!!.asExpression()
|
val filterExpression = filterCondition!!.asExpression(reformat)
|
||||||
val containsArgument = filterExpression.isFilterForContainsOperation(inputVariable, loop)
|
val containsArgument = filterExpression.isFilterForContainsOperation(inputVariable, loop)
|
||||||
return if (containsArgument != null) {
|
return if (containsArgument != null) {
|
||||||
val functionName = if (findFirst) "indexOf" else "lastIndexOf"
|
val functionName = if (findFirst) "indexOf" else "lastIndexOf"
|
||||||
@@ -260,7 +263,10 @@ object FindTransformationMatcher : TransformationMatcher {
|
|||||||
return object : FindOperationGenerator(this) {
|
return object : FindOperationGenerator(this) {
|
||||||
override fun generate(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
override fun generate(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
val generated = this@useElvisOperatorIfNeeded.generate(chainedCallGenerator)
|
val generated = this@useElvisOperatorIfNeeded.generate(chainedCallGenerator)
|
||||||
return KtPsiFactory(generated).createExpressionByPattern("$0\n ?: $1", generated, valueIfNotFound)
|
return KtPsiFactory(generated).createExpressionByPattern(
|
||||||
|
"$0\n ?: $1", generated, valueIfNotFound,
|
||||||
|
reformat = chainedCallGenerator.reformat
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -268,16 +274,16 @@ object FindTransformationMatcher : TransformationMatcher {
|
|||||||
when {
|
when {
|
||||||
valueIfFound.isVariableReference(inputVariable) -> {
|
valueIfFound.isVariableReference(inputVariable) -> {
|
||||||
val functionName = if (findFirst) "firstOrNull" else "lastOrNull"
|
val functionName = if (findFirst) "firstOrNull" else "lastOrNull"
|
||||||
val generator = SimpleGenerator(functionName, inputVariable, filterCondition?.asExpression())
|
val generator = SimpleGenerator(functionName, inputVariable, filterCondition?.asExpression(reformat))
|
||||||
return generator.useElvisOperatorIfNeeded()
|
return generator.useElvisOperatorIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
valueIfFound.isTrueConstant() && valueIfNotFound.isFalseConstant() -> {
|
valueIfFound.isTrueConstant() && valueIfNotFound.isFalseConstant() -> {
|
||||||
return buildFoundFlagGenerator(loop, inputVariable, filterCondition, negated = false)
|
return buildFoundFlagGenerator(loop, inputVariable, filterCondition, negated = false, reformat = reformat)
|
||||||
}
|
}
|
||||||
|
|
||||||
valueIfFound.isFalseConstant() && valueIfNotFound.isTrueConstant() -> {
|
valueIfFound.isFalseConstant() && valueIfNotFound.isTrueConstant() -> {
|
||||||
return buildFoundFlagGenerator(loop, inputVariable, filterCondition, negated = true)
|
return buildFoundFlagGenerator(loop, inputVariable, filterCondition, negated = true, reformat = reformat)
|
||||||
}
|
}
|
||||||
|
|
||||||
inputVariable.hasUsages(valueIfFound) -> {
|
inputVariable.hasUsages(valueIfFound) -> {
|
||||||
@@ -291,7 +297,7 @@ object FindTransformationMatcher : TransformationMatcher {
|
|||||||
if (receiver.isVariableReference(inputVariable) && selector != null && !inputVariable.hasUsages(selector)) {
|
if (receiver.isVariableReference(inputVariable) && selector != null && !inputVariable.hasUsages(selector)) {
|
||||||
return object: FindOperationGenerator("firstOrNull", filterCondition != null, chainCallCount = 2) {
|
return object: FindOperationGenerator("firstOrNull", filterCondition != null, chainCallCount = 2) {
|
||||||
override fun generate(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
override fun generate(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
val findFirstCall = generateChainedCall(functionName, chainedCallGenerator, inputVariable, filterCondition?.asExpression())
|
val findFirstCall = generateChainedCall(functionName, chainedCallGenerator, inputVariable, filterCondition?.asExpression(reformat))
|
||||||
return chainedCallGenerator.generate("$0", selector, receiver = findFirstCall, safeCall = true)
|
return chainedCallGenerator.generate("$0", selector, receiver = findFirstCall, safeCall = true)
|
||||||
}
|
}
|
||||||
}.useElvisOperatorIfNeeded()
|
}.useElvisOperatorIfNeeded()
|
||||||
@@ -303,19 +309,22 @@ object FindTransformationMatcher : TransformationMatcher {
|
|||||||
|
|
||||||
return object : FindOperationGenerator("firstOrNull", filterCondition != null, chainCallCount = 2 /* also includes "let" */) {
|
return object : FindOperationGenerator("firstOrNull", filterCondition != null, chainCallCount = 2 /* also includes "let" */) {
|
||||||
override fun generate(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
override fun generate(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
val findFirstCall = generateChainedCall(functionName, chainedCallGenerator, inputVariable, filterCondition?.asExpression())
|
val findFirstCall = generateChainedCall(functionName, chainedCallGenerator, inputVariable, filterCondition?.asExpression(reformat))
|
||||||
val letBody = generateLambda(inputVariable, valueIfFound)
|
val letBody = generateLambda(inputVariable, valueIfFound, chainedCallGenerator.reformat)
|
||||||
return chainedCallGenerator.generate("let $0:'{}'", letBody, receiver = findFirstCall, safeCall = true)
|
return chainedCallGenerator.generate("let $0:'{}'", letBody, receiver = findFirstCall, safeCall = true)
|
||||||
}
|
}
|
||||||
}.useElvisOperatorIfNeeded()
|
}.useElvisOperatorIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
val generator = buildFoundFlagGenerator(loop, inputVariable, filterCondition, negated = false)
|
val generator = buildFoundFlagGenerator(loop, inputVariable, filterCondition, negated = false, reformat = reformat)
|
||||||
return object : FindOperationGenerator(generator) {
|
return object : FindOperationGenerator(generator) {
|
||||||
override fun generate(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
override fun generate(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
val chainedCall = generator.generate(chainedCallGenerator)
|
val chainedCall = generator.generate(chainedCallGenerator)
|
||||||
return KtPsiFactory(chainedCall).createExpressionByPattern("if ($0) $1 else $2", chainedCall, valueIfFound, valueIfNotFound)
|
return KtPsiFactory(chainedCall).createExpressionByPattern(
|
||||||
|
"if ($0) $1 else $2", chainedCall, valueIfFound, valueIfNotFound,
|
||||||
|
reformat = chainedCallGenerator.reformat
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -327,13 +336,14 @@ object FindTransformationMatcher : TransformationMatcher {
|
|||||||
loop: KtForExpression,
|
loop: KtForExpression,
|
||||||
inputVariable: KtCallableDeclaration,
|
inputVariable: KtCallableDeclaration,
|
||||||
filter: Condition?,
|
filter: Condition?,
|
||||||
negated: Boolean
|
negated: Boolean,
|
||||||
|
reformat: Boolean
|
||||||
): FindOperationGenerator {
|
): FindOperationGenerator {
|
||||||
if (filter == null) {
|
if (filter == null) {
|
||||||
return SimpleGenerator(if (negated) "none" else "any", inputVariable, null)
|
return SimpleGenerator(if (negated) "none" else "any", inputVariable, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
val filterExpression = filter.asExpression()
|
val filterExpression = filter.asExpression(reformat)
|
||||||
val containsArgument = filterExpression.isFilterForContainsOperation(inputVariable, loop)
|
val containsArgument = filterExpression.isFilterForContainsOperation(inputVariable, loop)
|
||||||
if (containsArgument != null) {
|
if (containsArgument != null) {
|
||||||
val generator = SimpleGenerator("contains", inputVariable, null, containsArgument)
|
val generator = SimpleGenerator("contains", inputVariable, null, containsArgument)
|
||||||
@@ -349,7 +359,7 @@ object FindTransformationMatcher : TransformationMatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (filterExpression is KtPrefixExpression && filterExpression.operationToken == KtTokens.EXCL) {
|
if (filterExpression is KtPrefixExpression && filterExpression.operationToken == KtTokens.EXCL) {
|
||||||
return SimpleGenerator(if (negated) "any" else "none", inputVariable, filter.asNegatedExpression())
|
return SimpleGenerator(if (negated) "any" else "none", inputVariable, filter.asNegatedExpression(reformat))
|
||||||
}
|
}
|
||||||
|
|
||||||
return SimpleGenerator(if (negated) "none" else "any", inputVariable, filterExpression)
|
return SimpleGenerator(if (negated) "none" else "any", inputVariable, filterExpression)
|
||||||
|
|||||||
+1
-1
@@ -40,7 +40,7 @@ class ForEachTransformation(
|
|||||||
get() = functionName + "{}"
|
get() = functionName + "{}"
|
||||||
|
|
||||||
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
val lambda = generateLambda(inputVariable, indexVariable, statement)
|
val lambda = generateLambda(inputVariable, indexVariable, statement, chainedCallGenerator.reformat)
|
||||||
return chainedCallGenerator.generate("$functionName $0:'{}'", lambda)
|
return chainedCallGenerator.generate("$functionName $0:'{}'", lambda)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -33,7 +33,10 @@ class MaxOrMinTransformation(
|
|||||||
|
|
||||||
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
val call = chainedCallGenerator.generate(presentation)
|
val call = chainedCallGenerator.generate(presentation)
|
||||||
return KtPsiFactory(call).createExpressionByPattern("$0\n ?: $1", call, initialization.initializer)
|
return KtPsiFactory(call).createExpressionByPattern(
|
||||||
|
"$0\n ?: $1", call, initialization.initializer,
|
||||||
|
reformat = chainedCallGenerator.reformat
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+6
-3
@@ -40,7 +40,10 @@ abstract class SumTransformationBase(
|
|||||||
call
|
call
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
KtPsiFactory(call).createExpressionByPattern("$0 + $1", initialization.initializer, call)
|
KtPsiFactory(call).createExpressionByPattern(
|
||||||
|
"$0 + $1", initialization.initializer, call,
|
||||||
|
reformat = chainedCallGenerator.reformat
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +98,7 @@ abstract class SumTransformationBase(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val byExpression = if (conversionFunctionName != null)
|
val byExpression = if (conversionFunctionName != null)
|
||||||
KtPsiFactory(value).createExpressionByPattern("$0.$conversionFunctionName()", value)
|
KtPsiFactory(value).createExpressionByPattern("$0.$conversionFunctionName()", value, reformat = state.reformat)
|
||||||
else
|
else
|
||||||
value
|
value
|
||||||
|
|
||||||
@@ -171,7 +174,7 @@ class SumByTransformation(
|
|||||||
get() = "$functionName{}"
|
get() = "$functionName{}"
|
||||||
|
|
||||||
override fun generateCall(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
override fun generateCall(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
val lambda = generateLambda(inputVariable, byExpression)
|
val lambda = generateLambda(inputVariable, byExpression, chainedCallGenerator.reformat)
|
||||||
return chainedCallGenerator.generate("$functionName $0:'{}'", lambda)
|
return chainedCallGenerator.generate("$functionName $0:'{}'", lambda)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,8 +22,8 @@ import org.jetbrains.kotlin.lexer.KtTokens
|
|||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
|
||||||
interface Condition {
|
interface Condition {
|
||||||
fun asExpression(): KtExpression
|
fun asExpression(reformat: Boolean): KtExpression
|
||||||
fun asNegatedExpression(): KtExpression
|
fun asNegatedExpression(reformat: Boolean): KtExpression
|
||||||
fun toAtomicConditions(): List<AtomicCondition>
|
fun toAtomicConditions(): List<AtomicCondition>
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -62,28 +62,28 @@ class AtomicCondition(val expression: KtExpression, private val isNegated: Boole
|
|||||||
assert(expression.isPhysical)
|
assert(expression.isPhysical)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun asExpression() = if (isNegated) expression.negate() else expression
|
override fun asExpression(reformat: Boolean) = if (isNegated) expression.negate(reformat) else expression
|
||||||
override fun asNegatedExpression() = if (isNegated) expression else expression.negate()
|
override fun asNegatedExpression(reformat: Boolean) = if (isNegated) expression else expression.negate(reformat)
|
||||||
override fun toAtomicConditions() = listOf(this)
|
override fun toAtomicConditions() = listOf(this)
|
||||||
|
|
||||||
fun negate() = AtomicCondition(expression, !isNegated)
|
fun negate() = AtomicCondition(expression, !isNegated)
|
||||||
}
|
}
|
||||||
|
|
||||||
class CompositeCondition private constructor(val conditions: List<AtomicCondition>) : Condition {
|
class CompositeCondition private constructor(val conditions: List<AtomicCondition>) : Condition {
|
||||||
override fun asExpression(): KtExpression {
|
override fun asExpression(reformat: Boolean): KtExpression {
|
||||||
val factory = KtPsiFactory(conditions.first().expression)
|
val factory = KtPsiFactory(conditions.first().expression)
|
||||||
return factory.buildExpression {
|
return factory.buildExpression(reformat = reformat) {
|
||||||
for ((index, condition) in conditions.withIndex()) {
|
for ((index, condition) in conditions.withIndex()) {
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
appendFixedText("&&")
|
appendFixedText("&&")
|
||||||
}
|
}
|
||||||
appendExpression(condition.asExpression())
|
appendExpression(condition.asExpression(reformat))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun asNegatedExpression(): KtExpression {
|
override fun asNegatedExpression(reformat: Boolean): KtExpression {
|
||||||
return asExpression().negate()
|
return asExpression(reformat).negate()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toAtomicConditions() = conditions
|
override fun toAtomicConditions() = conditions
|
||||||
|
|||||||
+29
-16
@@ -97,7 +97,8 @@ abstract class FilterTransformationBase : SequenceTransformation {
|
|||||||
currentState.inputVariable,
|
currentState.inputVariable,
|
||||||
currentState.indexVariable,
|
currentState.indexVariable,
|
||||||
atomicConditions,
|
atomicConditions,
|
||||||
currentState.statements)
|
currentState.statements,
|
||||||
|
currentState.reformat)
|
||||||
assert(transformations.isNotEmpty())
|
assert(transformations.isNotEmpty())
|
||||||
|
|
||||||
val findTransformationMatch = FindTransformationMatcher.matchWithFilterBefore(currentState, transformations.last())
|
val findTransformationMatch = FindTransformationMatcher.matchWithFilterBefore(currentState, transformations.last())
|
||||||
@@ -115,13 +116,14 @@ abstract class FilterTransformationBase : SequenceTransformation {
|
|||||||
inputVariable: KtCallableDeclaration,
|
inputVariable: KtCallableDeclaration,
|
||||||
indexVariable: KtCallableDeclaration?,
|
indexVariable: KtCallableDeclaration?,
|
||||||
conditions: List<AtomicCondition>,
|
conditions: List<AtomicCondition>,
|
||||||
restStatements: List<KtExpression>
|
restStatements: List<KtExpression>,
|
||||||
|
reformat: Boolean
|
||||||
): List<FilterTransformationBase> {
|
): List<FilterTransformationBase> {
|
||||||
if (conditions.size == 1) {
|
if (conditions.size == 1) {
|
||||||
return listOf(createFilterTransformation(loop, inputVariable, indexVariable, conditions.single()))
|
return listOf(createFilterTransformation(loop, inputVariable, indexVariable, conditions.single(), reformat = reformat))
|
||||||
}
|
}
|
||||||
|
|
||||||
var transformations = conditions.map { createFilterTransformation(loop, inputVariable, indexVariable, it) }
|
var transformations = conditions.map { createFilterTransformation(loop, inputVariable, indexVariable, it, reformat = reformat) }
|
||||||
|
|
||||||
val resultTransformations = ArrayList<FilterTransformationBase>()
|
val resultTransformations = ArrayList<FilterTransformationBase>()
|
||||||
|
|
||||||
@@ -129,7 +131,7 @@ abstract class FilterTransformationBase : SequenceTransformation {
|
|||||||
if (lastUseOfIndex != null) {
|
if (lastUseOfIndex != null) {
|
||||||
val index = transformations.indexOf(lastUseOfIndex)
|
val index = transformations.indexOf(lastUseOfIndex)
|
||||||
val condition = CompositeCondition.create(conditions.take(index + 1))
|
val condition = CompositeCondition.create(conditions.take(index + 1))
|
||||||
resultTransformations.add(createFilterTransformation(loop, inputVariable, indexVariable, condition))
|
resultTransformations.add(createFilterTransformation(loop, inputVariable, indexVariable, condition, reformat = reformat))
|
||||||
transformations = transformations.drop(index + 1)
|
transformations = transformations.drop(index + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,11 +143,11 @@ abstract class FilterTransformationBase : SequenceTransformation {
|
|||||||
val prevFilter = resultTransformations.lastOrNull() as? FilterTransformation
|
val prevFilter = resultTransformations.lastOrNull() as? FilterTransformation
|
||||||
if (prevFilter != null) {
|
if (prevFilter != null) {
|
||||||
val mergedCondition = CompositeCondition.create(prevFilter.effectiveCondition.toAtomicConditions() + transformation.effectiveCondition.toAtomicConditions())
|
val mergedCondition = CompositeCondition.create(prevFilter.effectiveCondition.toAtomicConditions() + transformation.effectiveCondition.toAtomicConditions())
|
||||||
val mergedTransformation = createFilterTransformation(loop, inputVariable, indexVariable, mergedCondition, onlyFilterOrFilterNot = true)
|
val mergedTransformation = createFilterTransformation(loop, inputVariable, indexVariable, mergedCondition, onlyFilterOrFilterNot = true, reformat = reformat)
|
||||||
resultTransformations[resultTransformations.lastIndex] = mergedTransformation
|
resultTransformations[resultTransformations.lastIndex] = mergedTransformation
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
resultTransformations.add(createFilterTransformation(loop, inputVariable, indexVariable, condition, onlyFilterOrFilterNot = true))
|
resultTransformations.add(createFilterTransformation(loop, inputVariable, indexVariable, condition, onlyFilterOrFilterNot = true, reformat = reformat))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -188,7 +190,10 @@ abstract class FilterTransformationBase : SequenceTransformation {
|
|||||||
if (!state.inputVariable.hasUsages(condition) && (state.indexVariable == null || !state.indexVariable.hasUsages(condition))) return null
|
if (!state.inputVariable.hasUsages(condition) && (state.indexVariable == null || !state.indexVariable.hasUsages(condition))) return null
|
||||||
|
|
||||||
if (restStatements.isEmpty()) {
|
if (restStatements.isEmpty()) {
|
||||||
val transformation = createFilterTransformation(state.outerLoop, state.inputVariable, state.indexVariable, Condition.create(condition, negateCondition))
|
val transformation = createFilterTransformation(
|
||||||
|
state.outerLoop, state.inputVariable, state.indexVariable, Condition.create(condition, negateCondition),
|
||||||
|
reformat = state.reformat
|
||||||
|
)
|
||||||
val newState = state.copy(statements = listOf(then))
|
val newState = state.copy(statements = listOf(then))
|
||||||
return transformation to newState
|
return transformation to newState
|
||||||
}
|
}
|
||||||
@@ -197,14 +202,20 @@ abstract class FilterTransformationBase : SequenceTransformation {
|
|||||||
when (statement) {
|
when (statement) {
|
||||||
is KtContinueExpression -> {
|
is KtContinueExpression -> {
|
||||||
if (statement.targetLoop() != state.innerLoop) return null
|
if (statement.targetLoop() != state.innerLoop) return null
|
||||||
val transformation = createFilterTransformation(state.outerLoop, state.inputVariable, state.indexVariable, Condition.create(condition, !negateCondition))
|
val transformation = createFilterTransformation(
|
||||||
|
state.outerLoop, state.inputVariable, state.indexVariable, Condition.create(condition, !negateCondition),
|
||||||
|
reformat = state.reformat
|
||||||
|
)
|
||||||
val newState = state.copy(statements = restStatements)
|
val newState = state.copy(statements = restStatements)
|
||||||
return transformation to newState
|
return transformation to newState
|
||||||
}
|
}
|
||||||
|
|
||||||
is KtBreakExpression -> {
|
is KtBreakExpression -> {
|
||||||
if (statement.targetLoop() != state.outerLoop) return null
|
if (statement.targetLoop() != state.outerLoop) return null
|
||||||
val transformation = TakeWhileTransformation(state.outerLoop, state.inputVariable, if (negateCondition) condition else condition.negate())
|
val transformation = TakeWhileTransformation(
|
||||||
|
state.outerLoop, state.inputVariable,
|
||||||
|
if (negateCondition) condition else condition.negate(reformat = state.reformat)
|
||||||
|
)
|
||||||
val newState = state.copy(statements = restStatements)
|
val newState = state.copy(statements = restStatements)
|
||||||
return transformation to newState
|
return transformation to newState
|
||||||
}
|
}
|
||||||
@@ -219,14 +230,15 @@ abstract class FilterTransformationBase : SequenceTransformation {
|
|||||||
inputVariable: KtCallableDeclaration,
|
inputVariable: KtCallableDeclaration,
|
||||||
indexVariable: KtCallableDeclaration?,
|
indexVariable: KtCallableDeclaration?,
|
||||||
condition: Condition,
|
condition: Condition,
|
||||||
onlyFilterOrFilterNot: Boolean = false
|
onlyFilterOrFilterNot: Boolean = false,
|
||||||
|
reformat: Boolean
|
||||||
): FilterTransformationBase {
|
): FilterTransformationBase {
|
||||||
|
|
||||||
if (indexVariable != null && condition.hasUsagesOf(indexVariable)) {
|
if (indexVariable != null && condition.hasUsagesOf(indexVariable)) {
|
||||||
return FilterTransformation(loop, inputVariable, indexVariable, condition, isFilterNot = false)
|
return FilterTransformation(loop, inputVariable, indexVariable, condition, isFilterNot = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
val conditionAsExpression = condition.asExpression()
|
val conditionAsExpression = condition.asExpression(reformat)
|
||||||
if (!onlyFilterOrFilterNot) {
|
if (!onlyFilterOrFilterNot) {
|
||||||
if (conditionAsExpression is KtIsExpression
|
if (conditionAsExpression is KtIsExpression
|
||||||
&& !conditionAsExpression.isNegated
|
&& !conditionAsExpression.isNegated
|
||||||
@@ -288,10 +300,11 @@ class FilterTransformation(
|
|||||||
get() = "$functionName{}"
|
get() = "$functionName{}"
|
||||||
|
|
||||||
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
|
val reformat = chainedCallGenerator.reformat
|
||||||
val lambda = if (indexVariable != null)
|
val lambda = if (indexVariable != null)
|
||||||
generateLambda(inputVariable, indexVariable, effectiveCondition.asExpression())
|
generateLambda(inputVariable, indexVariable, effectiveCondition.asExpression(reformat), reformat)
|
||||||
else
|
else
|
||||||
generateLambda(inputVariable, if (isFilterNot) effectiveCondition.asNegatedExpression() else effectiveCondition.asExpression())
|
generateLambda(inputVariable, if (isFilterNot) effectiveCondition.asNegatedExpression(reformat) else effectiveCondition.asExpression(reformat), reformat)
|
||||||
return chainedCallGenerator.generate("$0$1:'{}'", functionName, lambda)
|
return chainedCallGenerator.generate("$0$1:'{}'", functionName, lambda)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -321,7 +334,7 @@ class FilterNotNullTransformation(
|
|||||||
|
|
||||||
override val indexVariable: KtCallableDeclaration? get() = null
|
override val indexVariable: KtCallableDeclaration? get() = null
|
||||||
|
|
||||||
override fun mergeWithPrevious(previousTransformation: SequenceTransformation): SequenceTransformation? {
|
override fun mergeWithPrevious(previousTransformation: SequenceTransformation, reformat: Boolean): SequenceTransformation? {
|
||||||
if (previousTransformation is MapTransformation) {
|
if (previousTransformation is MapTransformation) {
|
||||||
return MapTransformation(loop, previousTransformation.inputVariable, previousTransformation.indexVariable, previousTransformation.mapping, mapNotNull = true)
|
return MapTransformation(loop, previousTransformation.inputVariable, previousTransformation.indexVariable, previousTransformation.mapping, mapNotNull = true)
|
||||||
}
|
}
|
||||||
@@ -351,7 +364,7 @@ class TakeWhileTransformation(
|
|||||||
get() = "takeWhile{}"
|
get() = "takeWhile{}"
|
||||||
|
|
||||||
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
val lambda = generateLambda(inputVariable, condition)
|
val lambda = generateLambda(inputVariable, condition, chainedCallGenerator.reformat)
|
||||||
return chainedCallGenerator.generate("takeWhile$0:'{}'", lambda)
|
return chainedCallGenerator.generate("takeWhile$0:'{}'", lambda)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-6
@@ -36,7 +36,7 @@ class FlatMapTransformation(
|
|||||||
get() = "flatMap{}"
|
get() = "flatMap{}"
|
||||||
|
|
||||||
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
val lambda = generateLambda(inputVariable, transform)
|
val lambda = generateLambda(inputVariable, transform, chainedCallGenerator.reformat)
|
||||||
return chainedCallGenerator.generate("flatMap$0:'{}'", lambda)
|
return chainedCallGenerator.generate("flatMap$0:'{}'", lambda)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,8 +69,11 @@ class FlatMapTransformation(
|
|||||||
if (state.indexVariable != null && state.indexVariable.hasUsages(transform)) {
|
if (state.indexVariable != null && state.indexVariable.hasUsages(transform)) {
|
||||||
// if nested loop range uses index, convert to "mapIndexed {...}.flatMap { it }"
|
// if nested loop range uses index, convert to "mapIndexed {...}.flatMap { it }"
|
||||||
val mapIndexedTransformation = MapTransformation(state.outerLoop, state.inputVariable, state.indexVariable, transform, mapNotNull = false)
|
val mapIndexedTransformation = MapTransformation(state.outerLoop, state.inputVariable, state.indexVariable, transform, mapNotNull = false)
|
||||||
val inputVarExpression = KtPsiFactory(nestedLoop).createExpressionByPattern("$0", state.inputVariable.nameAsSafeName)
|
val inputVarExpression = KtPsiFactory(nestedLoop).createExpressionByPattern(
|
||||||
val transformToUse = if (state.lazySequence) inputVarExpression.asSequence() else inputVarExpression
|
"$0", state.inputVariable.nameAsSafeName,
|
||||||
|
reformat = state.reformat
|
||||||
|
)
|
||||||
|
val transformToUse = if (state.lazySequence) inputVarExpression.asSequence(state.reformat) else inputVarExpression
|
||||||
val flatMapTransformation = FlatMapTransformation(state.outerLoop, state.inputVariable, transformToUse)
|
val flatMapTransformation = FlatMapTransformation(state.outerLoop, state.inputVariable, transformToUse)
|
||||||
val newState = state.copy(
|
val newState = state.copy(
|
||||||
innerLoop = nestedLoop,
|
innerLoop = nestedLoop,
|
||||||
@@ -80,7 +83,7 @@ class FlatMapTransformation(
|
|||||||
return TransformationMatch.Sequence(listOf(mapIndexedTransformation, flatMapTransformation), newState)
|
return TransformationMatch.Sequence(listOf(mapIndexedTransformation, flatMapTransformation), newState)
|
||||||
}
|
}
|
||||||
|
|
||||||
val transformToUse = if (state.lazySequence) transform.asSequence() else transform
|
val transformToUse = if (state.lazySequence) transform.asSequence(state.reformat) else transform
|
||||||
val transformation = FlatMapTransformation(state.outerLoop, state.inputVariable, transformToUse)
|
val transformation = FlatMapTransformation(state.outerLoop, state.inputVariable, transformToUse)
|
||||||
val newState = state.copy(
|
val newState = state.copy(
|
||||||
innerLoop = nestedLoop,
|
innerLoop = nestedLoop,
|
||||||
@@ -90,8 +93,11 @@ class FlatMapTransformation(
|
|||||||
return TransformationMatch.Sequence(transformation, newState)
|
return TransformationMatch.Sequence(transformation, newState)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KtExpression.asSequence(): KtExpression {
|
private fun KtExpression.asSequence(reformat: Boolean): KtExpression {
|
||||||
return KtPsiFactory(this).createExpressionByPattern("$0.asSequence()", this)
|
return KtPsiFactory(this).createExpressionByPattern(
|
||||||
|
"$0.asSequence()", this,
|
||||||
|
reformat = reformat
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -40,7 +40,7 @@ class MapTransformation(
|
|||||||
get() = "$functionName{}"
|
get() = "$functionName{}"
|
||||||
|
|
||||||
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
val lambda = generateLambda(inputVariable, indexVariable, mapping)
|
val lambda = generateLambda(inputVariable, indexVariable, mapping, chainedCallGenerator.reformat)
|
||||||
return chainedCallGenerator.generate("$functionName$0:'{}'", lambda)
|
return chainedCallGenerator.generate("$functionName$0:'{}'", lambda)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,10 +44,13 @@ import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluat
|
|||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
fun generateLambda(inputVariable: KtCallableDeclaration, expression: KtExpression): KtLambdaExpression {
|
fun generateLambda(inputVariable: KtCallableDeclaration, expression: KtExpression, reformat: Boolean): KtLambdaExpression {
|
||||||
val psiFactory = KtPsiFactory(expression)
|
val psiFactory = KtPsiFactory(expression)
|
||||||
|
|
||||||
val lambdaExpression = psiFactory.createExpressionByPattern("{ $0 -> $1 }", inputVariable.nameAsSafeName, expression) as KtLambdaExpression
|
val lambdaExpression = psiFactory.createExpressionByPattern(
|
||||||
|
"{ $0 -> $1 }", inputVariable.nameAsSafeName, expression,
|
||||||
|
reformat = reformat
|
||||||
|
) as KtLambdaExpression
|
||||||
|
|
||||||
val isItUsedInside = expression.anyDescendantOfType<KtNameReferenceExpression> {
|
val isItUsedInside = expression.anyDescendantOfType<KtNameReferenceExpression> {
|
||||||
it.getQualifiedExpressionForSelector() == null && it.getReferencedName() == "it"
|
it.getQualifiedExpressionForSelector() == null && it.getReferencedName() == "it"
|
||||||
@@ -65,15 +68,20 @@ fun generateLambda(inputVariable: KtCallableDeclaration, expression: KtExpressio
|
|||||||
(usage.node as UserDataHolderBase).copyCopyableDataTo(replaced.node as UserDataHolderBase)
|
(usage.node as UserDataHolderBase).copyCopyableDataTo(replaced.node as UserDataHolderBase)
|
||||||
}
|
}
|
||||||
|
|
||||||
return psiFactory.createExpressionByPattern("{ $0 }", lambdaExpression.bodyExpression!!) as KtLambdaExpression
|
return psiFactory.createExpressionByPattern("{ $0 }", lambdaExpression.bodyExpression!!, reformat = reformat) as KtLambdaExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateLambda(inputVariable: KtCallableDeclaration, indexVariable: KtCallableDeclaration?, expression: KtExpression): KtLambdaExpression {
|
fun generateLambda(
|
||||||
|
inputVariable: KtCallableDeclaration,
|
||||||
|
indexVariable: KtCallableDeclaration?,
|
||||||
|
expression: KtExpression,
|
||||||
|
reformat: Boolean
|
||||||
|
): KtLambdaExpression {
|
||||||
if (indexVariable == null) {
|
if (indexVariable == null) {
|
||||||
return generateLambda(inputVariable, expression)
|
return generateLambda(inputVariable, expression, reformat)
|
||||||
}
|
}
|
||||||
|
|
||||||
val lambdaExpression = generateLambda(expression, *arrayOf(indexVariable, inputVariable))
|
val lambdaExpression = generateLambda(expression, *arrayOf(indexVariable, inputVariable), reformat = reformat)
|
||||||
|
|
||||||
// replace "index++" with "index" or "index + 1" (see IntroduceIndexMatcher)
|
// replace "index++" with "index" or "index + 1" (see IntroduceIndexMatcher)
|
||||||
val indexPlusPlus = lambdaExpression.findDescendantOfType<KtUnaryExpression> { unaryExpression ->
|
val indexPlusPlus = lambdaExpression.findDescendantOfType<KtUnaryExpression> { unaryExpression ->
|
||||||
@@ -89,23 +97,23 @@ fun generateLambda(inputVariable: KtCallableDeclaration, indexVariable: KtCallab
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (indexPlusPlus != null) {
|
if (indexPlusPlus != null) {
|
||||||
removePlusPlus(indexPlusPlus)
|
removePlusPlus(indexPlusPlus, reformat)
|
||||||
}
|
}
|
||||||
|
|
||||||
return lambdaExpression
|
return lambdaExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removePlusPlus(indexPlusPlus: KtUnaryExpression) {
|
fun removePlusPlus(indexPlusPlus: KtUnaryExpression, reformat: Boolean) {
|
||||||
val operand = indexPlusPlus.baseExpression!!
|
val operand = indexPlusPlus.baseExpression!!
|
||||||
val replacement = if (indexPlusPlus is KtPostfixExpression) // index++
|
val replacement = if (indexPlusPlus is KtPostfixExpression) // index++
|
||||||
operand
|
operand
|
||||||
else // ++index
|
else // ++index
|
||||||
KtPsiFactory(operand).createExpressionByPattern("$0 + 1", operand)
|
KtPsiFactory(operand).createExpressionByPattern("$0 + 1", operand, reformat = reformat)
|
||||||
indexPlusPlus.replace(replacement)
|
indexPlusPlus.replace(replacement)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateLambda(expression: KtExpression, vararg inputVariables: KtCallableDeclaration): KtLambdaExpression {
|
fun generateLambda(expression: KtExpression, vararg inputVariables: KtCallableDeclaration, reformat: Boolean): KtLambdaExpression {
|
||||||
return KtPsiFactory(expression).buildExpression {
|
return KtPsiFactory(expression).buildExpression(reformat = reformat) {
|
||||||
appendFixedText("{")
|
appendFixedText("{")
|
||||||
|
|
||||||
for ((index, variable) in inputVariables.withIndex()) {
|
for ((index, variable) in inputVariables.withIndex()) {
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ object J2KPostProcessingRegistrar {
|
|||||||
override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? {
|
override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||||
if (element !is KtCallExpression) return null
|
if (element !is KtCallExpression) return null
|
||||||
val propertyName = intention.detectPropertyNameToUse(element) ?: return null
|
val propertyName = intention.detectPropertyNameToUse(element) ?: return null
|
||||||
return { intention.applyTo(element, propertyName) }
|
return { intention.applyTo(element, propertyName, reformat = true) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user