Don't invoke formatter while checking availability of intentions

#KT-21632 Fixed
This commit is contained in:
Dmitry Jemerov
2017-12-13 19:00:40 +01:00
parent 54d626fe7d
commit 40184f053e
23 changed files with 210 additions and 126 deletions
@@ -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 {
@@ -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)
} }
} }
@@ -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 -> {
@@ -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) {
@@ -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)
} }
} }
} }
@@ -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())
@@ -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)
@@ -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)
} }
} }
@@ -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)
@@ -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)
} }
@@ -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
)
} }
/** /**
@@ -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
@@ -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)
} }
} }
@@ -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
)
} }
} }
} }
@@ -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) }
} }
} }