Minor: refactoring codeInliner package
This commit is contained in:
@@ -26,9 +26,9 @@ import org.jetbrains.kotlin.psi.*
|
|||||||
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis
|
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis
|
||||||
|
|
||||||
class ClassUsageReplacementStrategy(
|
class ClassUsageReplacementStrategy(
|
||||||
typeReplacement: KtUserType?,
|
typeReplacement: KtUserType?,
|
||||||
constructorReplacement: CodeToInline?,
|
constructorReplacement: CodeToInline?,
|
||||||
project: Project
|
project: Project
|
||||||
) : UsageReplacementStrategy {
|
) : UsageReplacementStrategy {
|
||||||
|
|
||||||
private val factory = KtPsiFactory(project)
|
private val factory = KtPsiFactory(project)
|
||||||
@@ -45,8 +45,7 @@ class ClassUsageReplacementStrategy(
|
|||||||
|
|
||||||
constructorReplacementStrategy?.createReplacer(usage)?.let { return it }
|
constructorReplacementStrategy?.createReplacer(usage)?.let { return it }
|
||||||
|
|
||||||
val parent = usage.parent
|
when (val parent = usage.parent) {
|
||||||
when (parent) {
|
|
||||||
is KtUserType -> {
|
is KtUserType -> {
|
||||||
if (typeReplacement == null) return null
|
if (typeReplacement == null) return null
|
||||||
return {
|
return {
|
||||||
@@ -91,8 +90,7 @@ class ClassUsageReplacementStrategy(
|
|||||||
|
|
||||||
val result = if (expressionToReplace != newExpression) {
|
val result = if (expressionToReplace != newExpression) {
|
||||||
expressionToReplace.replaced(newExpression)
|
expressionToReplace.replaced(newExpression)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
expressionToReplace
|
expressionToReplace
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -354,12 +354,11 @@ class CodeInliner<TCallElement : KtElement>(
|
|||||||
return Argument(valueAssigned, bindingContext.getType(valueAssigned))
|
return Argument(valueAssigned, bindingContext.getType(valueAssigned))
|
||||||
}
|
}
|
||||||
|
|
||||||
val resolvedArgument = resolvedCall.valueArguments[parameter]!!
|
when (val resolvedArgument = resolvedCall.valueArguments[parameter] ?: return null) {
|
||||||
when (resolvedArgument) {
|
|
||||||
is ExpressionValueArgument -> {
|
is ExpressionValueArgument -> {
|
||||||
val valueArgument = resolvedArgument.valueArgument!!
|
val valueArgument = resolvedArgument.valueArgument
|
||||||
val expression = valueArgument.getArgumentExpression()!!
|
val expression = valueArgument?.getArgumentExpression()
|
||||||
expression.mark(USER_CODE_KEY)
|
expression?.mark(USER_CODE_KEY) ?: return null
|
||||||
if (valueArgument is LambdaArgument) {
|
if (valueArgument is LambdaArgument) {
|
||||||
expression.mark(WAS_FUNCTION_LITERAL_ARGUMENT_KEY)
|
expression.mark(WAS_FUNCTION_LITERAL_ARGUMENT_KEY)
|
||||||
}
|
}
|
||||||
@@ -447,9 +446,9 @@ class CodeInliner<TCallElement : KtElement>(
|
|||||||
ShortenReferences { ShortenReferences.Options(removeThis = true) }.process(it, shortenFilter)
|
ShortenReferences { ShortenReferences.Options(removeThis = true) }.process(it, shortenFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
newElements.forEach {
|
newElements.forEach { element ->
|
||||||
// clean up user data
|
// clean up user data
|
||||||
it.forEachDescendantOfType<KtExpression> {
|
element.forEachDescendantOfType<KtExpression> {
|
||||||
it.clear(USER_CODE_KEY)
|
it.clear(USER_CODE_KEY)
|
||||||
it.clear(CodeToInline.PARAMETER_USAGE_KEY)
|
it.clear(CodeToInline.PARAMETER_USAGE_KEY)
|
||||||
it.clear(CodeToInline.TYPE_PARAMETER_USAGE_KEY)
|
it.clear(CodeToInline.TYPE_PARAMETER_USAGE_KEY)
|
||||||
@@ -457,7 +456,7 @@ class CodeInliner<TCallElement : KtElement>(
|
|||||||
it.clear(RECEIVER_VALUE_KEY)
|
it.clear(RECEIVER_VALUE_KEY)
|
||||||
it.clear(WAS_FUNCTION_LITERAL_ARGUMENT_KEY)
|
it.clear(WAS_FUNCTION_LITERAL_ARGUMENT_KEY)
|
||||||
}
|
}
|
||||||
it.forEachDescendantOfType<KtValueArgument> {
|
element.forEachDescendantOfType<KtValueArgument> {
|
||||||
it.clear(MAKE_ARGUMENT_NAMED_KEY)
|
it.clear(MAKE_ARGUMENT_NAMED_KEY)
|
||||||
it.clear(DEFAULT_PARAMETER_VALUE_KEY)
|
it.clear(DEFAULT_PARAMETER_VALUE_KEY)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,17 +45,17 @@ import org.jetbrains.kotlin.utils.sure
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class CodeToInlineBuilder(
|
class CodeToInlineBuilder(
|
||||||
private val targetCallable: CallableDescriptor,
|
private val targetCallable: CallableDescriptor,
|
||||||
private val resolutionFacade: ResolutionFacade
|
private val resolutionFacade: ResolutionFacade
|
||||||
) {
|
) {
|
||||||
private val psiFactory = KtPsiFactory(resolutionFacade.project)
|
private val psiFactory = KtPsiFactory(resolutionFacade.project)
|
||||||
|
|
||||||
//TODO: document that code will be modified
|
//TODO: document that code will be modified
|
||||||
fun prepareCodeToInline(
|
fun prepareCodeToInline(
|
||||||
mainExpression: KtExpression?,
|
mainExpression: KtExpression?,
|
||||||
statementsBefore: List<KtExpression>,
|
statementsBefore: List<KtExpression>,
|
||||||
analyze: () -> BindingContext,
|
analyze: () -> BindingContext,
|
||||||
reformat: Boolean
|
reformat: Boolean
|
||||||
): CodeToInline {
|
): CodeToInline {
|
||||||
var bindingContext = analyze()
|
var bindingContext = analyze()
|
||||||
|
|
||||||
@@ -113,8 +113,8 @@ class CodeToInlineBuilder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun needToAddParameterTypes(
|
private fun needToAddParameterTypes(
|
||||||
lambdaExpression: KtLambdaExpression,
|
lambdaExpression: KtLambdaExpression,
|
||||||
resolutionFacade: ResolutionFacade
|
resolutionFacade: ResolutionFacade
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val functionLiteral = lambdaExpression.functionLiteral
|
val functionLiteral = lambdaExpression.functionLiteral
|
||||||
val context = resolutionFacade.analyze(lambdaExpression, BodyResolveMode.PARTIAL_WITH_DIAGNOSTICS)
|
val context = resolutionFacade.analyze(lambdaExpression, BodyResolveMode.PARTIAL_WITH_DIAGNOSTICS)
|
||||||
@@ -122,15 +122,19 @@ class CodeToInlineBuilder(
|
|||||||
val factory = diagnostic.factory
|
val factory = diagnostic.factory
|
||||||
val element = diagnostic.psiElement
|
val element = diagnostic.psiElement
|
||||||
val hasCantInferParameter = factory == Errors.CANNOT_INFER_PARAMETER_TYPE &&
|
val hasCantInferParameter = factory == Errors.CANNOT_INFER_PARAMETER_TYPE &&
|
||||||
element.parent.parent == functionLiteral
|
element.parent.parent == functionLiteral
|
||||||
val hasUnresolvedItOrThis = factory == Errors.UNRESOLVED_REFERENCE &&
|
val hasUnresolvedItOrThis = factory == Errors.UNRESOLVED_REFERENCE &&
|
||||||
element.text == "it" &&
|
element.text == "it" &&
|
||||||
element.getStrictParentOfType<KtFunctionLiteral>() == functionLiteral
|
element.getStrictParentOfType<KtFunctionLiteral>() == functionLiteral
|
||||||
hasCantInferParameter || hasUnresolvedItOrThis
|
hasCantInferParameter || hasUnresolvedItOrThis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun insertExplicitTypeArguments(codeToInline: MutableCodeToInline, bindingContext: BindingContext, analyze: () -> BindingContext): BindingContext {
|
private fun insertExplicitTypeArguments(
|
||||||
|
codeToInline: MutableCodeToInline,
|
||||||
|
bindingContext: BindingContext,
|
||||||
|
analyze: () -> BindingContext
|
||||||
|
): BindingContext {
|
||||||
val typeArgsToAdd = ArrayList<Pair<KtCallExpression, KtTypeArgumentList>>()
|
val typeArgsToAdd = ArrayList<Pair<KtCallExpression, KtTypeArgumentList>>()
|
||||||
codeToInline.forEachDescendantOfType<KtCallExpression> {
|
codeToInline.forEachDescendantOfType<KtCallExpression> {
|
||||||
if (InsertExplicitTypeArgumentsIntention.isApplicableTo(it, bindingContext)) {
|
if (InsertExplicitTypeArgumentsIntention.isApplicableTo(it, bindingContext)) {
|
||||||
@@ -162,8 +166,7 @@ class CodeToInlineBuilder(
|
|||||||
if (expression.getReceiverExpression() == null) {
|
if (expression.getReceiverExpression() == null) {
|
||||||
if (target is ValueParameterDescriptor && target.containingDeclaration == targetCallable) {
|
if (target is ValueParameterDescriptor && target.containingDeclaration == targetCallable) {
|
||||||
expression.putCopyableUserData(CodeToInline.PARAMETER_USAGE_KEY, target.name)
|
expression.putCopyableUserData(CodeToInline.PARAMETER_USAGE_KEY, target.name)
|
||||||
}
|
} else if (target is TypeParameterDescriptor && target.containingDeclaration == targetCallable) {
|
||||||
else if (target is TypeParameterDescriptor && target.containingDeclaration == targetCallable) {
|
|
||||||
expression.putCopyableUserData(CodeToInline.TYPE_PARAMETER_USAGE_KEY, target.name)
|
expression.putCopyableUserData(CodeToInline.TYPE_PARAMETER_USAGE_KEY, target.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,9 +190,13 @@ class CodeToInlineBuilder(
|
|||||||
// add receivers in reverse order because arguments of a call were processed after the callee's name
|
// add receivers in reverse order because arguments of a call were processed after the callee's name
|
||||||
for ((expr, receiverExpression) in receiversToAdd.asReversed()) {
|
for ((expr, receiverExpression) in receiversToAdd.asReversed()) {
|
||||||
val expressionToReplace = expr.parent as? KtCallExpression ?: expr
|
val expressionToReplace = expr.parent as? KtCallExpression ?: expr
|
||||||
codeToInline.replaceExpression(expressionToReplace,
|
codeToInline.replaceExpression(
|
||||||
psiFactory.createExpressionByPattern("$0.$1", receiverExpression, expressionToReplace,
|
expressionToReplace,
|
||||||
reformat = reformat))
|
psiFactory.createExpressionByPattern(
|
||||||
|
"$0.$1", receiverExpression, expressionToReplace,
|
||||||
|
reformat = reformat
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -95,10 +95,10 @@ internal fun MutableCodeToInline.toNonMutable(): CodeToInline {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal inline fun <reified T : PsiElement> MutableCodeToInline.collectDescendantsOfType(noinline predicate: (T) -> Boolean = { true }): List<T> {
|
internal inline fun <reified T : PsiElement> MutableCodeToInline.collectDescendantsOfType(noinline predicate: (T) -> Boolean = { true }): List<T> {
|
||||||
return expressions.flatMap { it.collectDescendantsOfType<T>({ true }, predicate) }
|
return expressions.flatMap { it.collectDescendantsOfType({ true }, predicate) }
|
||||||
}
|
}
|
||||||
|
|
||||||
internal inline fun <reified T : PsiElement> MutableCodeToInline.forEachDescendantOfType(noinline action: (T) -> Unit) {
|
internal inline fun <reified T : PsiElement> MutableCodeToInline.forEachDescendantOfType(noinline action: (T) -> Unit) {
|
||||||
expressions.forEach { it.forEachDescendantOfType<T>(action) }
|
expressions.forEach { it.forEachDescendantOfType(action) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
internal abstract class ReplacementPerformer<TElement : KtElement>(
|
internal abstract class ReplacementPerformer<TElement : KtElement>(
|
||||||
protected val codeToInline: MutableCodeToInline,
|
protected val codeToInline: MutableCodeToInline,
|
||||||
protected var elementToBeReplaced: TElement
|
protected var elementToBeReplaced: TElement
|
||||||
) {
|
) {
|
||||||
protected val psiFactory = KtPsiFactory(elementToBeReplaced)
|
protected val psiFactory = KtPsiFactory(elementToBeReplaced)
|
||||||
|
|
||||||
@@ -43,8 +43,8 @@ internal abstract class ReplacementPerformer<TElement : KtElement>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal class AnnotationEntryReplacementPerformer(
|
internal class AnnotationEntryReplacementPerformer(
|
||||||
codeToInline: MutableCodeToInline,
|
codeToInline: MutableCodeToInline,
|
||||||
elementToBeReplaced: KtAnnotationEntry
|
elementToBeReplaced: KtAnnotationEntry
|
||||||
) : ReplacementPerformer<KtAnnotationEntry>(codeToInline, elementToBeReplaced) {
|
) : ReplacementPerformer<KtAnnotationEntry>(codeToInline, elementToBeReplaced) {
|
||||||
|
|
||||||
override fun doIt(postProcessing: (PsiChildRange) -> PsiChildRange): KtAnnotationEntry {
|
override fun doIt(postProcessing: (PsiChildRange) -> PsiChildRange): KtAnnotationEntry {
|
||||||
@@ -63,21 +63,22 @@ internal class AnnotationEntryReplacementPerformer(
|
|||||||
assert(range.first is KtAnnotationEntry)
|
assert(range.first is KtAnnotationEntry)
|
||||||
val annotationEntry = range.first as KtAnnotationEntry
|
val annotationEntry = range.first as KtAnnotationEntry
|
||||||
val text = annotationEntry.valueArguments.single().getArgumentExpression()!!.text
|
val text = annotationEntry.valueArguments.single().getArgumentExpression()!!.text
|
||||||
return annotationEntry.replaced(psiFactory.createAnnotationEntry("@" + text))
|
return annotationEntry.replaced(psiFactory.createAnnotationEntry("@$text"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class ExpressionReplacementPerformer(
|
internal class ExpressionReplacementPerformer(
|
||||||
codeToInline: MutableCodeToInline,
|
codeToInline: MutableCodeToInline,
|
||||||
expressionToBeReplaced: KtExpression
|
expressionToBeReplaced: KtExpression
|
||||||
) : ReplacementPerformer<KtExpression>(codeToInline, expressionToBeReplaced) {
|
) : ReplacementPerformer<KtExpression>(codeToInline, expressionToBeReplaced) {
|
||||||
|
|
||||||
fun KtExpression.replacedWithStringTemplate(templateExpression: KtStringTemplateExpression): KtExpression? {
|
fun KtExpression.replacedWithStringTemplate(templateExpression: KtStringTemplateExpression): KtExpression? {
|
||||||
val parent = this.parent
|
val parent = this.parent
|
||||||
|
|
||||||
return if (parent is KtStringTemplateEntryWithExpression
|
return if (parent is KtStringTemplateEntryWithExpression
|
||||||
// Do not mix raw and non-raw templates
|
// Do not mix raw and non-raw templates
|
||||||
&& parent.parent.firstChild.text == templateExpression.firstChild.text) {
|
&& parent.parent.firstChild.text == templateExpression.firstChild.text
|
||||||
|
) {
|
||||||
|
|
||||||
val entriesToAdd = templateExpression.entries
|
val entriesToAdd = templateExpression.entries
|
||||||
val grandParentTemplateExpression = parent.parent as KtStringTemplateExpression
|
val grandParentTemplateExpression = parent.parent as KtStringTemplateExpression
|
||||||
@@ -87,17 +88,16 @@ internal class ExpressionReplacementPerformer(
|
|||||||
val nextElement = parent.nextSibling
|
val nextElement = parent.nextSibling
|
||||||
if (lastNewEntry is KtSimpleNameStringTemplateEntry &&
|
if (lastNewEntry is KtSimpleNameStringTemplateEntry &&
|
||||||
lastNewEntry.expression != null &&
|
lastNewEntry.expression != null &&
|
||||||
!canPlaceAfterSimpleNameEntry(nextElement)) {
|
!canPlaceAfterSimpleNameEntry(nextElement)
|
||||||
|
) {
|
||||||
lastNewEntry.replace(KtPsiFactory(this).createBlockStringTemplateEntry(lastNewEntry.expression!!))
|
lastNewEntry.replace(KtPsiFactory(this).createBlockStringTemplateEntry(lastNewEntry.expression!!))
|
||||||
}
|
}
|
||||||
grandParentTemplateExpression
|
grandParentTemplateExpression
|
||||||
}
|
} else null
|
||||||
else null
|
|
||||||
|
|
||||||
parent.delete()
|
parent.delete()
|
||||||
result
|
result
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
replaced(templateExpression)
|
replaced(templateExpression)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,8 +129,7 @@ internal class ExpressionReplacementPerformer(
|
|||||||
if (canDropElementToBeReplaced) {
|
if (canDropElementToBeReplaced) {
|
||||||
stub.delete()
|
stub.delete()
|
||||||
null
|
null
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
stub.replaced(psiFactory.createExpression("Unit"))
|
stub.replaced(psiFactory.createExpression("Unit"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,17 +140,14 @@ internal class ExpressionReplacementPerformer(
|
|||||||
var range = if (replaced != null) {
|
var range = if (replaced != null) {
|
||||||
if (insertedStatements.isEmpty()) {
|
if (insertedStatements.isEmpty()) {
|
||||||
PsiChildRange.singleElement(replaced)
|
PsiChildRange.singleElement(replaced)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
val statement = insertedStatements.first()
|
val statement = insertedStatements.first()
|
||||||
PsiChildRange(statement, replaced.parentsWithSelf.first { it.parent == statement.parent })
|
PsiChildRange(statement, replaced.parentsWithSelf.first { it.parent == statement.parent })
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
if (insertedStatements.isEmpty()) {
|
if (insertedStatements.isEmpty()) {
|
||||||
PsiChildRange.EMPTY
|
PsiChildRange.EMPTY
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
PsiChildRange(insertedStatements.first(), insertedStatements.last())
|
PsiChildRange(insertedStatements.first(), insertedStatements.last())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,8 +156,7 @@ internal class ExpressionReplacementPerformer(
|
|||||||
listener?.attach()
|
listener?.attach()
|
||||||
try {
|
try {
|
||||||
range = postProcessing(range)
|
range = postProcessing(range)
|
||||||
}
|
} finally {
|
||||||
finally {
|
|
||||||
listener?.detach()
|
listener?.detach()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,8 +209,8 @@ internal class ExpressionReplacementPerformer(
|
|||||||
val runAfterReplacement = elementToBeReplaced.replaced(runExpression)
|
val runAfterReplacement = elementToBeReplaced.replaced(runExpression)
|
||||||
val ktLambdaArgument = runAfterReplacement.lambdaArguments[0]
|
val ktLambdaArgument = runAfterReplacement.lambdaArguments[0]
|
||||||
val block = ktLambdaArgument.getLambdaExpression()?.bodyExpression
|
val block = ktLambdaArgument.getLambdaExpression()?.bodyExpression
|
||||||
?: throw KotlinExceptionWithAttachments("cant get body expression for $ktLambdaArgument")
|
?: throw KotlinExceptionWithAttachments("cant get body expression for $ktLambdaArgument")
|
||||||
.withAttachment("ktLambdaArgument", ktLambdaArgument.text)
|
.withAttachment("ktLambdaArgument", ktLambdaArgument.text)
|
||||||
elementToBeReplaced = block.statements.single()
|
elementToBeReplaced = block.statements.single()
|
||||||
return elementToBeReplaced
|
return elementToBeReplaced
|
||||||
|
|
||||||
@@ -231,7 +226,7 @@ internal class ExpressionReplacementPerformer(
|
|||||||
private fun <TElement : KtElement> withElementToBeReplacedPreserved(action: () -> TElement): TElement {
|
private fun <TElement : KtElement> withElementToBeReplacedPreserved(action: () -> TElement): TElement {
|
||||||
elementToBeReplaced.putCopyableUserData(ELEMENT_TO_BE_REPLACED_KEY, Unit)
|
elementToBeReplaced.putCopyableUserData(ELEMENT_TO_BE_REPLACED_KEY, Unit)
|
||||||
val result = action()
|
val result = action()
|
||||||
elementToBeReplaced = result.findDescendantOfType<KtExpression> { it.getCopyableUserData(ELEMENT_TO_BE_REPLACED_KEY) != null }!!
|
elementToBeReplaced = result.findDescendantOfType { it.getCopyableUserData(ELEMENT_TO_BE_REPLACED_KEY) != null }!!
|
||||||
elementToBeReplaced.putCopyableUserData(ELEMENT_TO_BE_REPLACED_KEY, null)
|
elementToBeReplaced.putCopyableUserData(ELEMENT_TO_BE_REPLACED_KEY, null)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,72 +51,72 @@ interface UsageReplacementStrategy {
|
|||||||
private val LOG = Logger.getInstance(UsageReplacementStrategy::class.java)
|
private val LOG = Logger.getInstance(UsageReplacementStrategy::class.java)
|
||||||
|
|
||||||
fun UsageReplacementStrategy.replaceUsagesInWholeProject(
|
fun UsageReplacementStrategy.replaceUsagesInWholeProject(
|
||||||
targetPsiElement: PsiElement,
|
targetPsiElement: PsiElement,
|
||||||
progressTitle: String,
|
progressTitle: String,
|
||||||
commandName: String,
|
commandName: String,
|
||||||
postAction: () -> Unit = {}
|
postAction: () -> Unit = {}
|
||||||
) {
|
) {
|
||||||
val project = targetPsiElement.project
|
val project = targetPsiElement.project
|
||||||
ProgressManager.getInstance().run(
|
ProgressManager.getInstance().run(
|
||||||
object : Task.Modal(project, progressTitle, true) {
|
object : Task.Modal(project, progressTitle, true) {
|
||||||
override fun run(indicator: ProgressIndicator) {
|
override fun run(indicator: ProgressIndicator) {
|
||||||
val usages = runReadAction {
|
val usages = runReadAction {
|
||||||
val searchScope = KotlinSourceFilterScope.projectSources(GlobalSearchScope.projectScope(project), project)
|
val searchScope = KotlinSourceFilterScope.projectSources(GlobalSearchScope.projectScope(project), project)
|
||||||
ReferencesSearch.search(targetPsiElement, searchScope)
|
ReferencesSearch.search(targetPsiElement, searchScope)
|
||||||
.filterIsInstance<KtSimpleNameReference>()
|
.filterIsInstance<KtSimpleNameReference>()
|
||||||
.map { ref -> ref.expression }
|
.map { ref -> ref.expression }
|
||||||
}
|
|
||||||
this@replaceUsagesInWholeProject.replaceUsages(usages, targetPsiElement, project, commandName, postAction)
|
|
||||||
}
|
}
|
||||||
})
|
this@replaceUsagesInWholeProject.replaceUsages(usages, targetPsiElement, project, commandName, postAction)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun UsageReplacementStrategy.replaceUsages(
|
fun UsageReplacementStrategy.replaceUsages(
|
||||||
usages: Collection<KtSimpleNameExpression>,
|
usages: Collection<KtSimpleNameExpression>,
|
||||||
targetPsiElement: PsiElement,
|
targetPsiElement: PsiElement,
|
||||||
project: Project,
|
project: Project,
|
||||||
commandName: String,
|
commandName: String,
|
||||||
postAction: () -> Unit = {}
|
postAction: () -> Unit = {}
|
||||||
) {
|
) {
|
||||||
GuiUtils.invokeLaterIfNeeded({
|
GuiUtils.invokeLaterIfNeeded({
|
||||||
project.executeWriteCommand(commandName) {
|
project.executeWriteCommand(commandName) {
|
||||||
val targetDeclaration = targetPsiElement as? KtNamedDeclaration
|
val targetDeclaration = targetPsiElement as? KtNamedDeclaration
|
||||||
|
|
||||||
val usagesByFile = usages.groupBy { it.containingFile }
|
val usagesByFile = usages.groupBy { it.containingFile }
|
||||||
|
|
||||||
val KEY = Key<Unit>("UsageReplacementStrategy.replaceUsages")
|
val KEY = Key<Unit>("UsageReplacementStrategy.replaceUsages")
|
||||||
|
|
||||||
for ((file, usagesInFile) in usagesByFile) {
|
for ((file, usagesInFile) in usagesByFile) {
|
||||||
usagesInFile.forEach { it.putCopyableUserData(KEY, Unit) }
|
usagesInFile.forEach { it.putCopyableUserData(KEY, Unit) }
|
||||||
|
|
||||||
// we should delete imports later to not affect other usages
|
// we should delete imports later to not affect other usages
|
||||||
val importsToDelete = mutableListOf<KtImportDirective>()
|
val importsToDelete = mutableListOf<KtImportDirective>()
|
||||||
|
|
||||||
var usagesToProcess = usagesInFile
|
var usagesToProcess = usagesInFile
|
||||||
while (usagesToProcess.isNotEmpty()) {
|
while (usagesToProcess.isNotEmpty()) {
|
||||||
if (processUsages(usagesToProcess, targetDeclaration, importsToDelete)) break
|
if (processUsages(usagesToProcess, targetDeclaration, importsToDelete)) break
|
||||||
|
|
||||||
// some usages may get invalidated we need to find them in the tree
|
// some usages may get invalidated we need to find them in the tree
|
||||||
usagesToProcess = file.collectDescendantsOfType<KtSimpleNameExpression> { it.getCopyableUserData(KEY) != null }
|
usagesToProcess = file.collectDescendantsOfType { it.getCopyableUserData(KEY) != null }
|
||||||
}
|
}
|
||||||
|
|
||||||
file.forEachDescendantOfType<KtSimpleNameExpression> { it.putCopyableUserData(KEY, null) }
|
file.forEachDescendantOfType<KtSimpleNameExpression> { it.putCopyableUserData(KEY, null) }
|
||||||
|
|
||||||
importsToDelete.forEach { it.delete() }
|
|
||||||
}
|
|
||||||
|
|
||||||
postAction()
|
importsToDelete.forEach { it.delete() }
|
||||||
}
|
}
|
||||||
}, ModalityState.NON_MODAL)
|
|
||||||
|
postAction()
|
||||||
|
}
|
||||||
|
}, ModalityState.NON_MODAL)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return false if some usages were invalidated
|
* @return false if some usages were invalidated
|
||||||
*/
|
*/
|
||||||
private fun UsageReplacementStrategy.processUsages(
|
private fun UsageReplacementStrategy.processUsages(
|
||||||
usages: List<KtSimpleNameExpression>,
|
usages: List<KtSimpleNameExpression>,
|
||||||
targetDeclaration: KtNamedDeclaration?,
|
targetDeclaration: KtNamedDeclaration?,
|
||||||
importsToDelete: MutableList<KtImportDirective>
|
importsToDelete: MutableList<KtImportDirective>
|
||||||
): Boolean {
|
): Boolean {
|
||||||
var invalidUsagesFound = false
|
var invalidUsagesFound = false
|
||||||
for (usage in usages) {
|
for (usage in usages) {
|
||||||
@@ -138,17 +138,18 @@ private fun UsageReplacementStrategy.processUsages(
|
|||||||
}
|
}
|
||||||
|
|
||||||
createReplacer(usage)?.invoke()
|
createReplacer(usage)?.invoke()
|
||||||
}
|
} catch (e: Throwable) {
|
||||||
catch (e: Throwable) {
|
|
||||||
LOG.error(e)
|
LOG.error(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return !invalidUsagesFound
|
return !invalidUsagesFound
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun UsageReplacementStrategy.specialUsageProcessing(usage: KtSimpleNameExpression, targetDeclaration: KtNamedDeclaration?): Boolean {
|
private fun UsageReplacementStrategy.specialUsageProcessing(
|
||||||
val usageParent = usage.parent
|
usage: KtSimpleNameExpression,
|
||||||
when (usageParent) {
|
targetDeclaration: KtNamedDeclaration?
|
||||||
|
): Boolean {
|
||||||
|
when (val usageParent = usage.parent) {
|
||||||
is KtCallableReferenceExpression -> {
|
is KtCallableReferenceExpression -> {
|
||||||
val grandParent = usageParent.parent
|
val grandParent = usageParent.parent
|
||||||
ConvertReferenceToLambdaIntention().applyTo(usageParent, null)
|
ConvertReferenceToLambdaIntention().applyTo(usageParent, null)
|
||||||
@@ -166,7 +167,7 @@ private fun UsageReplacementStrategy.specialUsageProcessing(usage: KtSimpleNameE
|
|||||||
for (lambdaArgument in lambdaArguments) {
|
for (lambdaArgument in lambdaArguments) {
|
||||||
val lambdaExpression = lambdaArgument.getLambdaExpression() ?: continue
|
val lambdaExpression = lambdaArgument.getLambdaExpression() ?: continue
|
||||||
val functionDescriptor =
|
val functionDescriptor =
|
||||||
lambdaExpression.functionLiteral.resolveToDescriptorIfAny() as? FunctionDescriptor ?: continue
|
lambdaExpression.functionLiteral.resolveToDescriptorIfAny() as? FunctionDescriptor ?: continue
|
||||||
if (functionDescriptor.valueParameters.isNotEmpty()) {
|
if (functionDescriptor.valueParameters.isNotEmpty()) {
|
||||||
specifySignature.applyTo(lambdaExpression, null)
|
specifySignature.applyTo(lambdaExpression, null)
|
||||||
}
|
}
|
||||||
@@ -183,7 +184,7 @@ private fun UsageReplacementStrategy.specialUsageProcessing(usage: KtSimpleNameE
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun UsageReplacementStrategy.doRefactoringInside(
|
private fun UsageReplacementStrategy.doRefactoringInside(
|
||||||
element: KtElement, targetName: String?, targetDescriptor: DeclarationDescriptor?
|
element: KtElement, targetName: String?, targetDescriptor: DeclarationDescriptor?
|
||||||
) {
|
) {
|
||||||
element.forEachDescendantOfType<KtSimpleNameExpression> { usage ->
|
element.forEachDescendantOfType<KtSimpleNameExpression> { usage ->
|
||||||
if (usage.isValid && usage.getReferencedName() == targetName) {
|
if (usage.isValid && usage.getReferencedName() == targetName) {
|
||||||
|
|||||||
@@ -50,12 +50,12 @@ import org.jetbrains.kotlin.types.KotlinType
|
|||||||
* @param safeCall If true, then the whole code must not be executed if the [value] evaluates to null.
|
* @param safeCall If true, then the whole code must not be executed if the [value] evaluates to null.
|
||||||
*/
|
*/
|
||||||
internal fun MutableCodeToInline.introduceValue(
|
internal fun MutableCodeToInline.introduceValue(
|
||||||
value: KtExpression,
|
value: KtExpression,
|
||||||
valueType: KotlinType?,
|
valueType: KotlinType?,
|
||||||
usages: Collection<KtExpression>,
|
usages: Collection<KtExpression>,
|
||||||
expressionToBeReplaced: KtExpression,
|
expressionToBeReplaced: KtExpression,
|
||||||
nameSuggestion: String? = null,
|
nameSuggestion: String? = null,
|
||||||
safeCall: Boolean = false
|
safeCall: Boolean = false
|
||||||
) {
|
) {
|
||||||
assert(usages.all { this.containsStrictlyInside(it) })
|
assert(usages.all { this.containsStrictlyInside(it) })
|
||||||
|
|
||||||
@@ -100,12 +100,10 @@ internal fun MutableCodeToInline.introduceValue(
|
|||||||
}
|
}
|
||||||
|
|
||||||
replaceUsages(name)
|
replaceUsages(name)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
statementsBefore.add(0, value)
|
statementsBefore.add(0, value)
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
val useIt = !isNameUsed("it")
|
val useIt = !isNameUsed("it")
|
||||||
val name = if (useIt) Name.identifier("it") else suggestName { !isNameUsed(it) }
|
val name = if (useIt) Name.identifier("it") else suggestName { !isNameUsed(it) }
|
||||||
replaceUsages(name)
|
replaceUsages(name)
|
||||||
@@ -139,17 +137,17 @@ fun String.nameHasConflictsInScope(lexicalScope: LexicalScope) = lexicalScope.ge
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun variableNeedsExplicitType(
|
private fun variableNeedsExplicitType(
|
||||||
initializer: KtExpression,
|
initializer: KtExpression,
|
||||||
initializerType: KotlinType,
|
initializerType: KotlinType,
|
||||||
context: KtExpression,
|
context: KtExpression,
|
||||||
resolutionScope: LexicalScope,
|
resolutionScope: LexicalScope,
|
||||||
bindingContext: BindingContext
|
bindingContext: BindingContext
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (ErrorUtils.containsErrorType(initializerType)) return false
|
if (ErrorUtils.containsErrorType(initializerType)) return false
|
||||||
val valueTypeWithoutExpectedType = initializer.computeTypeInContext(
|
val valueTypeWithoutExpectedType = initializer.computeTypeInContext(
|
||||||
resolutionScope,
|
resolutionScope,
|
||||||
context,
|
context,
|
||||||
dataFlowInfo = bindingContext.getDataFlowInfoBefore(context)
|
dataFlowInfo = bindingContext.getDataFlowInfoBefore(context)
|
||||||
)
|
)
|
||||||
return valueTypeWithoutExpectedType == null || ErrorUtils.containsErrorType(valueTypeWithoutExpectedType)
|
return valueTypeWithoutExpectedType == null || ErrorUtils.containsErrorType(valueTypeWithoutExpectedType)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user