Inline Variable: Move string template processing to InlineValHandler as it violates contract of the PsiElement.replace()

This commit is contained in:
Alexey Sedunov
2015-12-14 11:56:34 +03:00
parent b5fad71b18
commit 3dfb9d2e5e
2 changed files with 40 additions and 30 deletions
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.psi
import com.intellij.lang.ASTNode import com.intellij.lang.ASTNode
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.KtNodeType import org.jetbrains.kotlin.KtNodeType
import org.jetbrains.kotlin.psi.psiUtil.canPlaceAfterSimpleNameEntry
public abstract class KtExpressionImpl(node: ASTNode) : KtElementImpl(node), KtExpression { public abstract class KtExpressionImpl(node: ASTNode) : KtElementImpl(node), KtExpression {
@@ -39,33 +38,13 @@ public abstract class KtExpressionImpl(node: ASTNode) : KtElementImpl(node), KtE
val parent = expression.getParent() val parent = expression.getParent()
if (newElement is KtExpression) { if (newElement is KtExpression) {
when { when (parent) {
parent is KtStringTemplateEntryWithExpression && is KtExpression, is KtValueArgument -> {
newElement is KtStringTemplateExpression &&
// Do not mix raw and non-raw templates
parent.parent.firstChild.text == newElement.firstChild.text -> {
val entriesToAdd = newElement.entries
val templateExpression = parent.parent as KtStringTemplateExpression
if (entriesToAdd.size > 0) {
templateExpression.addRangeBefore(entriesToAdd.first(), entriesToAdd.last(), parent)
val lastNewEntry = parent.prevSibling
val nextElement = parent.nextSibling
if (lastNewEntry is KtSimpleNameStringTemplateEntry &&
lastNewEntry.expression != null &&
!canPlaceAfterSimpleNameEntry(nextElement)) {
lastNewEntry.replace(KtPsiFactory(expression).createBlockStringTemplateEntry(lastNewEntry.expression!!))
}
}
parent.delete()
}
parent is KtExpression || parent is KtValueArgument -> {
if (KtPsiUtil.areParenthesesNecessary(newElement, expression, parent as KtElement)) { if (KtPsiUtil.areParenthesesNecessary(newElement, expression, parent as KtElement)) {
return rawReplaceHandler(KtPsiFactory(expression).createExpressionByPattern("($0)", newElement)) return rawReplaceHandler(KtPsiFactory(expression).createExpressionByPattern("($0)", newElement))
} }
} }
is KtSimpleNameStringTemplateEntry -> {
parent is KtSimpleNameStringTemplateEntry -> {
if (newElement !is KtSimpleNameExpression) { if (newElement !is KtSimpleNameExpression) {
val newEntry = parent.replace(KtPsiFactory(expression).createBlockStringTemplateEntry(newElement)) as KtBlockStringTemplateEntry val newEntry = parent.replace(KtPsiFactory(expression).createBlockStringTemplateEntry(newElement)) as KtBlockStringTemplateEntry
return newEntry.getExpression()!! return newEntry.getExpression()!!
@@ -45,9 +45,7 @@ import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.ShortenReferences import org.jetbrains.kotlin.idea.util.ShortenReferences
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getCallWithAssert import org.jetbrains.kotlin.resolve.calls.callUtil.getCallWithAssert
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
@@ -55,6 +53,7 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
import org.jetbrains.kotlin.utils.sure import org.jetbrains.kotlin.utils.sure
import java.util.* import java.util.*
@@ -66,6 +65,38 @@ class KotlinInlineValHandler : InlineActionHandler() {
return element.getter == null && element.receiverTypeReference == null return element.getter == null && element.receiverTypeReference == null
} }
private fun doReplace(expression: KtExpression, replacement: KtExpression): List<KtExpression> {
val parent = expression.parent
if (parent is KtStringTemplateEntryWithExpression &&
replacement is KtStringTemplateExpression &&
// Do not mix raw and non-raw templates
parent.parent.firstChild.text == replacement.firstChild.text) {
val entriesToAdd = replacement.entries
val templateExpression = parent.parent as KtStringTemplateExpression
val inlinedExpressions = if (entriesToAdd.size > 0) {
val firstAddedEntry = templateExpression.addRangeBefore(entriesToAdd.first(), entriesToAdd.last(), parent)
val lastNewEntry = parent.prevSibling
val nextElement = parent.nextSibling
if (lastNewEntry is KtSimpleNameStringTemplateEntry &&
lastNewEntry.expression != null &&
!canPlaceAfterSimpleNameEntry(nextElement)) {
lastNewEntry.replace(KtPsiFactory(expression).createBlockStringTemplateEntry(lastNewEntry.expression!!))
}
firstAddedEntry.siblings()
.take(entriesToAdd.size)
.mapNotNull { (it as? KtStringTemplateEntryWithExpression)?.expression }
.toList()
}
else emptyList()
parent.delete()
return inlinedExpressions
}
return expression.replaced(replacement).singletonList()
}
override fun inlineElement(project: Project, editor: Editor, element: PsiElement) { override fun inlineElement(project: Project, editor: Editor, element: PsiElement) {
val declaration = element as KtProperty val declaration = element as KtProperty
val file = declaration.getContainingKtFile() val file = declaration.getContainingKtFile()
@@ -130,9 +161,9 @@ class KotlinInlineValHandler : InlineActionHandler() {
} }
project.executeWriteCommand(RefactoringBundle.message("inline.command", name)) { project.executeWriteCommand(RefactoringBundle.message("inline.command", name)) {
val inlinedExpressions = referenceExpressions.mapNotNull { referenceExpression -> val inlinedExpressions = referenceExpressions.flatMap { referenceExpression ->
if (assignments.contains(referenceExpression.parent)) return@mapNotNull null if (assignments.contains(referenceExpression.parent)) return@flatMap emptyList<KtExpression>()
referenceExpression.replaced(initializer) doReplace(referenceExpression, initializer)
} }
assignments.forEach { it.delete() } assignments.forEach { it.delete() }