Fix flakiness of createByPattern results

Fix the order of pointers and place logical operators first

#KTIJ-28165 Fixed


Merge-request: KT-MR-14447
Merged-by: Vladimir Dolzhenko <Vladimir.Dolzhenko@jetbrains.com>
This commit is contained in:
Vladimir Dolzhenko
2024-02-19 12:16:09 +00:00
committed by Space Team
parent 302e38dd18
commit 60b4ce2c36
@@ -24,13 +24,13 @@ 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.NonNls import org.jetbrains.annotations.NonNls
import org.jetbrains.annotations.TestOnly import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.lexer.KtTokens
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
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.renderer.render import org.jetbrains.kotlin.renderer.render
import java.util.*
fun KtPsiFactory.createExpressionByPattern(@NonNls pattern: String, @NonNls vararg args: Any, reformat: Boolean = true): KtExpression = fun KtPsiFactory.createExpressionByPattern(@NonNls pattern: String, @NonNls vararg args: Any, reformat: Boolean = true): KtExpression =
createByPattern(pattern, *args, reformat = reformat) { createExpression(it) } createByPattern(pattern, *args, reformat = reformat) { createExpression(it) }
@@ -151,7 +151,7 @@ fun <TElement : KtElement> createByPattern(
val pointerManager = SmartPointerManager.getInstance(project) val pointerManager = SmartPointerManager.getInstance(project)
val pointers = HashMap<SmartPsiElementPointer<PsiElement>, Int>() val pointers = LinkedHashMap<SmartPsiElementPointer<PsiElement>, Int>()
PlaceholdersLoop@ PlaceholdersLoop@
for ((n, placeholders) in allPlaceholders) { for ((n, placeholders) in allPlaceholders) {
@@ -182,7 +182,7 @@ fun <TElement : KtElement> createByPattern(
if (CREATE_BY_PATTERN_MAY_NOT_REFORMAT) { if (CREATE_BY_PATTERN_MAY_NOT_REFORMAT) {
throw java.lang.IllegalArgumentException("Reformatting is not allowed in the current context; please change the invocation to use reformat=false") 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.asSequence()
.filter { args[it.key] is String } .filter { args[it.key] is String }
.flatMap { it.value } .flatMap { it.value }
.map { it.range } .map { it.range }
@@ -205,18 +205,30 @@ fun <TElement : KtElement> createByPattern(
// do not reformat the whole expression in PostprocessReformattingAspect // do not reformat the whole expression in PostprocessReformattingAspect
CodeEditUtil.setNodeGeneratedRecursively(resultElement.node, false) CodeEditUtil.setNodeGeneratedRecursively(resultElement.node, false)
} }
for ((pointer, n) in pointers) {
var element = pointer.element!!
if (element is KtFunctionLiteral) {
element = element.parent as KtLambdaExpression
}
@Suppress("UNCHECKED_CAST")
val argumentType = argumentTypes[n] as PsiElementPlaceholderArgumentType<in Any, in PsiElement>
val range = argumentType.replacePlaceholderElement(element, args[n], reformat)
if (element == resultElement) { // it is needed to place logical operators first for expressions like `xyz xyz xyz` to become `xyz && xyz`
assert(range.first == range.last) // otherwise if when we place an expression we have to wrap it with extra parenthesis => it becomes `(a != null) xyz xyz`
resultElement = range.first as KtElement val (left, right) = pointers.entries.partition {
val n = it.value
val elementType = (args[n] as? KtOperationReferenceExpression)?.getReferencedNameElementType()
elementType == KtTokens.ANDAND || elementType == KtTokens.OROR
}
for (partition in listOf(left, right)) {
for ((pointer, n) in partition) {
var element = pointer.element!!
if (element is KtFunctionLiteral) {
element = element.parent as KtLambdaExpression
}
@Suppress("UNCHECKED_CAST")
val argumentType = argumentTypes[n] as PsiElementPlaceholderArgumentType<in Any, in PsiElement>
val range = argumentType.replacePlaceholderElement(element, args[n], reformat)
if (element == resultElement) {
assert(range.first == range.last)
resultElement = range.first as KtElement
}
} }
} }