Refactored ConstructedExpressionWrapper into ConstructedExpressionHolder merging it with CallKindHandler
This commit is contained in:
-61
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.replacement
|
||||
|
||||
import org.jetbrains.kotlin.idea.core.replaced
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis
|
||||
|
||||
interface CallKindHandler<TCallElement : KtElement> {
|
||||
fun elementToReplace(callElement: TCallElement): KtElement
|
||||
|
||||
fun precheckReplacementPattern(pattern: ReplacementExpression): Boolean
|
||||
|
||||
fun wrapGeneratedExpression(expression: KtExpression): TCallElement
|
||||
|
||||
fun unwrapResult(result: TCallElement): TCallElement
|
||||
}
|
||||
|
||||
object CallExpressionHandler : CallKindHandler<KtExpression> {
|
||||
override fun elementToReplace(callElement: KtExpression) = callElement.getQualifiedExpressionForSelectorOrThis()
|
||||
|
||||
override fun precheckReplacementPattern(pattern: ReplacementExpression) = true
|
||||
|
||||
override fun wrapGeneratedExpression(expression: KtExpression) = expression
|
||||
|
||||
override fun unwrapResult(result: KtExpression) = result
|
||||
}
|
||||
|
||||
object AnnotationEntryHandler : CallKindHandler<KtAnnotationEntry> {
|
||||
override fun elementToReplace(callElement: KtAnnotationEntry) = callElement
|
||||
|
||||
override fun precheckReplacementPattern(pattern: ReplacementExpression): Boolean {
|
||||
//TODO
|
||||
return true
|
||||
}
|
||||
|
||||
//TODO: how to prohibit wrapping replacement expression into anything?
|
||||
|
||||
override fun wrapGeneratedExpression(expression: KtExpression): KtAnnotationEntry {
|
||||
return createByPattern("@Dummy($0)", expression) { KtPsiFactory(expression).createAnnotationEntry(it) }
|
||||
}
|
||||
|
||||
override fun unwrapResult(result: KtAnnotationEntry): KtAnnotationEntry {
|
||||
val text = result.valueArguments.single().getArgumentExpression()!!.text
|
||||
return result.replaced(KtPsiFactory(result).createAnnotationEntry("@" + text))
|
||||
}
|
||||
}
|
||||
+3
-13
@@ -34,9 +34,9 @@ class CallableUsageReplacementStrategy(
|
||||
if (!resolvedCall.isReallySuccess()) return null
|
||||
|
||||
val callElement = resolvedCall.call.callElement
|
||||
val callTypeHandler = callTypeHandler(callElement) ?: return null
|
||||
if (callElement !is KtExpression && callElement !is KtAnnotationEntry) return null
|
||||
|
||||
if (!callTypeHandler.precheckReplacementPattern(replacement)) return null
|
||||
//TODO: precheck pattern correctness for annotation entry
|
||||
|
||||
return {
|
||||
if (usage is KtOperationReferenceExpression && usage.getReferencedNameElementType() != KtTokens.IDENTIFIER) {
|
||||
@@ -46,19 +46,9 @@ class CallableUsageReplacementStrategy(
|
||||
else {
|
||||
// copy replacement expression because it is modified by performCallReplacement
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
ReplacementEngine.performCallReplacement(usage, bindingContext, resolvedCall, callElement,
|
||||
callTypeHandler as CallKindHandler<KtElement>,
|
||||
replacement.copy())
|
||||
ReplacementEngine.performCallReplacement(usage, bindingContext, resolvedCall, callElement, replacement.copy())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun callTypeHandler(callElement: KtElement): CallKindHandler<*>? {
|
||||
return when (callElement) {
|
||||
is KtExpression -> CallExpressionHandler
|
||||
is KtAnnotationEntry -> AnnotationEntryHandler
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+75
-21
@@ -19,10 +19,12 @@ package org.jetbrains.kotlin.idea.replacement
|
||||
import org.jetbrains.kotlin.idea.analysis.computeTypeInContext
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
||||
import org.jetbrains.kotlin.idea.core.replaced
|
||||
import org.jetbrains.kotlin.idea.core.setType
|
||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.PsiChildRange
|
||||
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
||||
@@ -34,8 +36,9 @@ import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import java.util.*
|
||||
|
||||
internal open class ConstructedExpressionWrapper(
|
||||
internal abstract class ConstructedCodeHolder<TElement>(
|
||||
var expression: KtExpression,
|
||||
val elementToBeReplaced: TElement,
|
||||
val bindingContext: BindingContext
|
||||
) {
|
||||
val psiFactory = KtPsiFactory(expression)
|
||||
@@ -50,14 +53,68 @@ internal open class ConstructedExpressionWrapper(
|
||||
return oldExpression.replace(newExpression) as KtExpression
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun finish(postProcessing: (PsiChildRange) -> PsiChildRange): TElement
|
||||
}
|
||||
|
||||
internal class ConstructedExpressionWrapperWithIntroduceFeature(
|
||||
internal class ConstructedAnnotationEntryHolder(
|
||||
expression: KtExpression,
|
||||
bindingContext: BindingContext,
|
||||
val expressionToBeReplaced: KtExpression
|
||||
) : ConstructedExpressionWrapper(expression, bindingContext) {
|
||||
val addedStatements = ArrayList<KtExpression>()
|
||||
elementToBeReplaced: KtAnnotationEntry,
|
||||
bindingContext: BindingContext
|
||||
) : ConstructedCodeHolder<KtAnnotationEntry>(expression, elementToBeReplaced, bindingContext) {
|
||||
|
||||
override fun finish(postProcessing: (PsiChildRange) -> PsiChildRange): KtAnnotationEntry {
|
||||
val dummyAnnotationEntry = createByPattern("@Dummy($0)", expression) { psiFactory.createAnnotationEntry(it) }
|
||||
val replaced = elementToBeReplaced.replace(dummyAnnotationEntry)
|
||||
var range = PsiChildRange.singleElement(replaced)
|
||||
range = postProcessing(range)
|
||||
|
||||
assert(range.first == range.last)
|
||||
assert(range.first is KtAnnotationEntry)
|
||||
val annotationEntry = range.first as KtAnnotationEntry
|
||||
val text = annotationEntry.valueArguments.single().getArgumentExpression()!!.text
|
||||
return annotationEntry.replaced(psiFactory.createAnnotationEntry("@" + text))
|
||||
}
|
||||
}
|
||||
|
||||
internal class ConstructedExpressionHolder(
|
||||
expression: KtExpression,
|
||||
expressionToBeReplaced: KtExpression,
|
||||
bindingContext: BindingContext
|
||||
) : ConstructedCodeHolder<KtExpression>(expression, expressionToBeReplaced, bindingContext) {
|
||||
|
||||
private data class StatementToInsert<TStatement : KtExpression>(val statement: TStatement, val postProcessing: (TStatement) -> Unit)
|
||||
|
||||
private val statementsToInsert = ArrayList<StatementToInsert<*>>()
|
||||
|
||||
private fun <TStatement : KtExpression> addStatementToInsert(statement: TStatement, postProcessing: (TStatement) -> Unit = {}) {
|
||||
statementsToInsert.add(StatementToInsert(statement, postProcessing))
|
||||
}
|
||||
|
||||
override fun finish(postProcessing: (PsiChildRange) -> PsiChildRange): KtExpression {
|
||||
val insertedStatements = ArrayList<KtExpression>()
|
||||
for (toInsert in statementsToInsert.asReversed()) { //TODO: do we need it?
|
||||
val block = elementToBeReplaced.parent as KtBlockExpression //TODO
|
||||
|
||||
val inserted = block.addBefore(toInsert.statement, elementToBeReplaced) as KtExpression
|
||||
block.addBefore(psiFactory.createNewLine(), elementToBeReplaced)
|
||||
insertedStatements.add(inserted)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
(toInsert.postProcessing as (KtExpression) -> Unit).invoke(inserted)
|
||||
}
|
||||
|
||||
val replaced = elementToBeReplaced.replace(expression)
|
||||
|
||||
var range = if (insertedStatements.isEmpty())
|
||||
PsiChildRange.singleElement(replaced)
|
||||
else
|
||||
PsiChildRange(insertedStatements.first(), replaced)
|
||||
|
||||
range = postProcessing(range)
|
||||
|
||||
return range.last as KtExpression
|
||||
}
|
||||
|
||||
fun introduceValue(
|
||||
value: KtExpression,
|
||||
@@ -87,17 +144,17 @@ internal class ConstructedExpressionWrapperWithIntroduceFeature(
|
||||
fun isNameUsed(name: String) = collectNameUsages(expression, name).any { nameUsage -> usages.none { it.isAncestor(nameUsage) } }
|
||||
|
||||
if (!safeCall) {
|
||||
val block = expressionToBeReplaced.parent as? KtBlockExpression
|
||||
val block = elementToBeReplaced.parent as? KtBlockExpression
|
||||
if (block != null) {
|
||||
val resolutionScope = expressionToBeReplaced.getResolutionScope(bindingContext, expressionToBeReplaced.getResolutionFacade())
|
||||
val resolutionScope = elementToBeReplaced.getResolutionScope(bindingContext, elementToBeReplaced.getResolutionFacade())
|
||||
|
||||
if (usages.isNotEmpty()) {
|
||||
var explicitType: KotlinType? = null
|
||||
if (valueType != null && !ErrorUtils.containsErrorType(valueType)) {
|
||||
val valueTypeWithoutExpectedType = value.computeTypeInContext(
|
||||
resolutionScope,
|
||||
expressionToBeReplaced,
|
||||
dataFlowInfo = bindingContext.getDataFlowInfo(expressionToBeReplaced)
|
||||
elementToBeReplaced,
|
||||
dataFlowInfo = bindingContext.getDataFlowInfo(elementToBeReplaced)
|
||||
)
|
||||
if (valueTypeWithoutExpectedType == null || ErrorUtils.containsErrorType(valueTypeWithoutExpectedType)) {
|
||||
explicitType = valueType
|
||||
@@ -108,21 +165,18 @@ internal class ConstructedExpressionWrapperWithIntroduceFeature(
|
||||
resolutionScope.findLocalVariable(Name.identifier(name)) == null && !isNameUsed(name)
|
||||
}
|
||||
|
||||
var declaration = psiFactory.createDeclarationByPattern<KtVariableDeclaration>("val $0 = $1", name, value)
|
||||
declaration = block.addBefore(declaration, expressionToBeReplaced) as KtVariableDeclaration
|
||||
block.addBefore(psiFactory.createNewLine(), expressionToBeReplaced)
|
||||
|
||||
if (explicitType != null) {
|
||||
declaration.setType(explicitType)
|
||||
}
|
||||
val declaration = psiFactory.createDeclarationByPattern<KtVariableDeclaration>("val $0 = $1", name, value)
|
||||
addStatementToInsert(declaration,
|
||||
postProcessing = {
|
||||
if (explicitType != null) {
|
||||
it.setType(explicitType!!)
|
||||
}
|
||||
})
|
||||
|
||||
replaceUsages(name)
|
||||
|
||||
addedStatements.add(declaration)
|
||||
}
|
||||
else {
|
||||
addedStatements.add(block.addBefore(value, expressionToBeReplaced) as KtExpression)
|
||||
block.addBefore(psiFactory.createNewLine(), expressionToBeReplaced)
|
||||
addStatementToInsert(value)
|
||||
}
|
||||
return
|
||||
}
|
||||
+31
-37
@@ -52,15 +52,18 @@ object ReplacementEngine {
|
||||
bindingContext: BindingContext,
|
||||
resolvedCall: ResolvedCall<out CallableDescriptor>,
|
||||
callElement: TCallElement,
|
||||
callKindHandler: CallKindHandler<TCallElement>,
|
||||
replacement: ReplacementExpression
|
||||
): KtElement {
|
||||
val project = element.project
|
||||
val psiFactory = KtPsiFactory(project)
|
||||
val descriptor = resolvedCall.resultingDescriptor
|
||||
val file = element.getContainingKtFile()
|
||||
|
||||
val qualifiedExpression = callElement.getQualifiedExpressionForSelector()
|
||||
val elementToBeReplaced = callKindHandler.elementToReplace(callElement)
|
||||
val elementToBeReplaced = when (callElement) {
|
||||
is KtExpression -> callElement.getQualifiedExpressionForSelectorOrThis()
|
||||
else -> callElement
|
||||
}
|
||||
|
||||
val commentSaver = CommentSaver(elementToBeReplaced, saveLineBreaks = true)
|
||||
|
||||
@@ -78,68 +81,59 @@ object ReplacementEngine {
|
||||
|
||||
receiver?.mark(RECEIVER_VALUE_KEY)
|
||||
|
||||
val wrapper = if (elementToBeReplaced is KtExpression)
|
||||
ConstructedExpressionWrapperWithIntroduceFeature(replacement.expression, bindingContext, elementToBeReplaced)
|
||||
else
|
||||
ConstructedExpressionWrapper(replacement.expression, bindingContext)
|
||||
val codeHolder = when (elementToBeReplaced) {
|
||||
is KtExpression -> ConstructedExpressionHolder(replacement.expression, elementToBeReplaced, bindingContext)
|
||||
is KtAnnotationEntry -> ConstructedAnnotationEntryHolder(replacement.expression, elementToBeReplaced, bindingContext)
|
||||
else -> error("Unsupported element")
|
||||
}
|
||||
|
||||
//TODO: this@
|
||||
for (thisExpression in replacement.expression.collectDescendantsOfType<KtThisExpression>()) {
|
||||
if (receiver != null) {
|
||||
wrapper.replaceExpression(thisExpression, receiver)
|
||||
codeHolder.replaceExpression(thisExpression, receiver)
|
||||
}
|
||||
else {
|
||||
thisExpression.mark(RECEIVER_VALUE_KEY)
|
||||
}
|
||||
}
|
||||
|
||||
val introduceValuesForParameters = wrapper.processValueParameterUsages(resolvedCall, project)
|
||||
val introduceValuesForParameters = codeHolder.processValueParameterUsages(resolvedCall, project)
|
||||
|
||||
wrapper.processTypeParameterUsages(resolvedCall)
|
||||
codeHolder.processTypeParameterUsages(resolvedCall)
|
||||
|
||||
if (qualifiedExpression is KtSafeQualifiedExpression) {
|
||||
(wrapper as ConstructedExpressionWrapperWithIntroduceFeature).wrapExpressionForSafeCall(receiver!!, receiverType)
|
||||
(codeHolder as ConstructedExpressionHolder).wrapExpressionForSafeCall(receiver!!, receiverType)
|
||||
}
|
||||
else if (callElement is KtBinaryExpression && callElement.operationToken == KtTokens.IDENTIFIER) {
|
||||
wrapper.keepInfixFormIfPossible()
|
||||
codeHolder.keepInfixFormIfPossible()
|
||||
}
|
||||
|
||||
if (receiver != null && wrapper is ConstructedExpressionWrapperWithIntroduceFeature) {
|
||||
val thisReplaced = wrapper.expression.collectDescendantsOfType<KtExpression> { it[RECEIVER_VALUE_KEY] }
|
||||
if (receiver != null && codeHolder is ConstructedExpressionHolder) {
|
||||
val thisReplaced = codeHolder.expression.collectDescendantsOfType<KtExpression> { it[RECEIVER_VALUE_KEY] }
|
||||
if (receiver.shouldKeepValue(thisReplaced.size)) {
|
||||
wrapper.introduceValue(receiver, receiverType, thisReplaced)
|
||||
codeHolder.introduceValue(receiver, receiverType, thisReplaced)
|
||||
}
|
||||
}
|
||||
|
||||
if (wrapper is ConstructedExpressionWrapperWithIntroduceFeature) {
|
||||
if (codeHolder is ConstructedExpressionHolder) {
|
||||
for ((parameter, value, valueType) in introduceValuesForParameters) {
|
||||
val usagesReplaced = wrapper.expression.collectDescendantsOfType<KtExpression> { it[PARAMETER_VALUE_KEY] == parameter }
|
||||
wrapper.introduceValue(value, valueType, usagesReplaced, nameSuggestion = parameter.name.asString())
|
||||
val usagesReplaced = codeHolder.expression.collectDescendantsOfType<KtExpression> { it[PARAMETER_VALUE_KEY] == parameter }
|
||||
codeHolder.introduceValue(value, valueType, usagesReplaced, nameSuggestion = parameter.name.asString())
|
||||
}
|
||||
}
|
||||
|
||||
val wrappedExpression = callKindHandler.wrapGeneratedExpression(wrapper.expression)
|
||||
val result = elementToBeReplaced.replace(wrappedExpression) as KtElement
|
||||
|
||||
val file = result.getContainingKtFile()
|
||||
replacement.fqNamesToImport
|
||||
.flatMap { file.resolveImportReference(it) }
|
||||
.forEach { ImportInsertHelper.getInstance(project).importDescriptor(file, it) }
|
||||
|
||||
var resultRange = if (wrapper !is ConstructedExpressionWrapperWithIntroduceFeature || wrapper.addedStatements.isEmpty())
|
||||
PsiChildRange.singleElement(result)
|
||||
else
|
||||
PsiChildRange(wrapper.addedStatements.first(), result)
|
||||
|
||||
resultRange = postProcessInsertedCode(resultRange)
|
||||
|
||||
commentSaver.restore(resultRange)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return callKindHandler.unwrapResult(resultRange.last as TCallElement)
|
||||
return codeHolder.finish { range ->
|
||||
val newRange = postProcessInsertedCode(range)
|
||||
commentSaver.restore(newRange)
|
||||
newRange
|
||||
}
|
||||
}
|
||||
|
||||
private fun ConstructedExpressionWrapper.processValueParameterUsages(
|
||||
private fun ConstructedCodeHolder<*>.processValueParameterUsages(
|
||||
resolvedCall: ResolvedCall<out CallableDescriptor>,
|
||||
project: Project
|
||||
): Collection<IntroduceValueForParameter> {
|
||||
@@ -182,7 +176,7 @@ object ReplacementEngine {
|
||||
val value: KtExpression,
|
||||
val valueType: KotlinType?)
|
||||
|
||||
private fun ConstructedExpressionWrapper.processTypeParameterUsages(resolvedCall: ResolvedCall<out CallableDescriptor>) {
|
||||
private fun ConstructedCodeHolder<*>.processTypeParameterUsages(resolvedCall: ResolvedCall<out CallableDescriptor>) {
|
||||
val typeParameters = resolvedCall.resultingDescriptor.original.typeParameters
|
||||
|
||||
val callElement = resolvedCall.call.callElement
|
||||
@@ -230,7 +224,7 @@ object ReplacementEngine {
|
||||
}
|
||||
}
|
||||
|
||||
private fun ConstructedExpressionWrapperWithIntroduceFeature.wrapExpressionForSafeCall(receiver: KtExpression, receiverType: KotlinType?) {
|
||||
private fun ConstructedExpressionHolder.wrapExpressionForSafeCall(receiver: KtExpression, receiverType: KotlinType?) {
|
||||
val qualified = expression as? KtQualifiedExpression
|
||||
if (qualified != null) {
|
||||
if (qualified.receiverExpression[RECEIVER_VALUE_KEY]) {
|
||||
@@ -243,7 +237,7 @@ object ReplacementEngine {
|
||||
}
|
||||
}
|
||||
|
||||
if (expressionToBeReplaced.isUsedAsExpression(bindingContext)) {
|
||||
if (elementToBeReplaced.isUsedAsExpression(bindingContext)) {
|
||||
val thisReplaced = expression.collectDescendantsOfType<KtExpression> { it[RECEIVER_VALUE_KEY] }
|
||||
introduceValue(receiver, receiverType, thisReplaced, safeCall = true)
|
||||
}
|
||||
@@ -252,7 +246,7 @@ object ReplacementEngine {
|
||||
}
|
||||
}
|
||||
|
||||
private fun ConstructedExpressionWrapper.keepInfixFormIfPossible() {
|
||||
private fun ConstructedCodeHolder<*>.keepInfixFormIfPossible() {
|
||||
val dotQualified = expression as? KtDotQualifiedExpression ?: return
|
||||
val receiver = dotQualified.receiverExpression
|
||||
if (!receiver[RECEIVER_VALUE_KEY]) return
|
||||
|
||||
Reference in New Issue
Block a user