Unify usage of ProgressManager.progressIndicator
This commit is contained in:
+1
@@ -280,6 +280,7 @@
|
|||||||
<list>
|
<list>
|
||||||
<Problem reference="com.intellij.testFramework.PlatformTestCase#createModuleAt" reason="Not static anymore in 181 after 7dacf096c47d2125e17031c71a037b63ab00ec53" />
|
<Problem reference="com.intellij.testFramework.PlatformTestCase#createModuleAt" reason="Not static anymore in 181 after 7dacf096c47d2125e17031c71a037b63ab00ec53" />
|
||||||
<Problem reference="com.intellij.testFramework.PlatformTestCase#doCreateRealModuleIn" reason="Not static anymore in 181 after 7dacf096c47d2125e17031c71a037b63ab00ec53" />
|
<Problem reference="com.intellij.testFramework.PlatformTestCase#doCreateRealModuleIn" reason="Not static anymore in 181 after 7dacf096c47d2125e17031c71a037b63ab00ec53" />
|
||||||
|
<Problem reference="com.intellij.openapi.progress.ProgressManager#getProgressIndicator" reason="Nullable in 181. Temporary use progressIndicatorNullable instead." />
|
||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ package org.jetbrains.kotlin.idea.util.application
|
|||||||
|
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.command.CommandProcessor
|
import com.intellij.openapi.command.CommandProcessor
|
||||||
|
import com.intellij.openapi.progress.ProgressIndicator
|
||||||
|
import com.intellij.openapi.progress.ProgressIndicatorProvider
|
||||||
|
import com.intellij.openapi.progress.ProgressManager
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
|
|
||||||
fun <T> runReadAction(action: () -> T): T {
|
fun <T> runReadAction(action: () -> T): T {
|
||||||
@@ -41,4 +44,10 @@ fun <T> Project.executeCommand(name: String, groupId: Any? = null, command: () -
|
|||||||
CommandProcessor.getInstance().executeCommand(this, { result = command() }, name, groupId)
|
CommandProcessor.getInstance().executeCommand(this, { result = command() }, name, groupId)
|
||||||
@Suppress("USELESS_CAST")
|
@Suppress("USELESS_CAST")
|
||||||
return result as T
|
return result as T
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProgressManager.getProgressIndicator() is nullable in 181 and dynamic again in 182
|
||||||
|
* BUNCH: 181
|
||||||
|
*/
|
||||||
|
val ProgressManager.progressIndicatorNullable: ProgressIndicator? get() = progressIndicator
|
||||||
+2
-1
@@ -29,6 +29,7 @@ import com.intellij.psi.PsiModifier
|
|||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
|
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
|
||||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.progressIndicatorNullable
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
import org.jetbrains.kotlin.load.java.isFromJava
|
import org.jetbrains.kotlin.load.java.isFromJava
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
@@ -54,7 +55,7 @@ class SearchNotPropertyCandidatesAction : AnAction() {
|
|||||||
ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
||||||
{
|
{
|
||||||
runReadAction {
|
runReadAction {
|
||||||
ProgressManager.getInstance().progressIndicator.isIndeterminate = true
|
ProgressManager.getInstance().progressIndicatorNullable!!.isIndeterminate = true
|
||||||
processAllDescriptors(packageDesc, project)
|
processAllDescriptors(packageDesc, project)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
-164
@@ -1,164 +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.actions.internal
|
|
||||||
|
|
||||||
|
|
||||||
import com.intellij.openapi.actionSystem.AnAction
|
|
||||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
|
||||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
|
||||||
import com.intellij.openapi.progress.ProgressManager
|
|
||||||
import com.intellij.openapi.progress.ProgressManager.progress
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.openapi.ui.Messages
|
|
||||||
import com.intellij.psi.PsiMethod
|
|
||||||
import com.intellij.psi.PsiModifier
|
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
|
|
||||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
|
||||||
import org.jetbrains.kotlin.load.java.isFromJava
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
|
||||||
|
|
||||||
|
|
||||||
class SearchNotPropertyCandidatesAction : AnAction() {
|
|
||||||
override fun actionPerformed(e: AnActionEvent?) {
|
|
||||||
val project = e?.project!!
|
|
||||||
val psiFile = e.getData(CommonDataKeys.PSI_FILE) as? KtFile ?: return
|
|
||||||
|
|
||||||
val desc = psiFile.findModuleDescriptor()
|
|
||||||
val result = Messages.showInputDialog("Enter package FqName", "Search for Not Property candidates", Messages.getQuestionIcon())
|
|
||||||
val packageDesc = try {
|
|
||||||
val fqName = FqName.fromSegments(result!!.split('.'))
|
|
||||||
desc.getPackage(fqName)
|
|
||||||
}
|
|
||||||
catch (e: Exception) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ProgressManager.getInstance().runProcessWithProgressSynchronously(
|
|
||||||
{
|
|
||||||
runReadAction {
|
|
||||||
ProgressManager.getInstance().progressIndicator!!.isIndeterminate = true
|
|
||||||
processAllDescriptors(packageDesc, project)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Searching for Not Property candidates",
|
|
||||||
true,
|
|
||||||
project)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun processAllDescriptors(desc: DeclarationDescriptor, project: Project) {
|
|
||||||
val processed = mutableSetOf<DeclarationDescriptor>()
|
|
||||||
var pFunctions = 0
|
|
||||||
val matchedDescriptors = mutableSetOf<FunctionDescriptor>()
|
|
||||||
|
|
||||||
fun recursive(desc: DeclarationDescriptor) {
|
|
||||||
if (desc in processed) return
|
|
||||||
progress("Step 1: Collecting ${processed.size}:$pFunctions:${matchedDescriptors.size}", "$desc")
|
|
||||||
when (desc) {
|
|
||||||
|
|
||||||
is ModuleDescriptor -> recursive(desc.getPackage(FqName("java")))
|
|
||||||
is ClassDescriptor -> desc.unsubstitutedMemberScope.getContributedDescriptors { true }.forEach(::recursive)
|
|
||||||
is PackageViewDescriptor -> desc.memberScope.getContributedDescriptors { true }.forEach(::recursive)
|
|
||||||
|
|
||||||
is FunctionDescriptor -> {
|
|
||||||
if (desc.isFromJava) {
|
|
||||||
val name = desc.fqNameUnsafe.shortName().asString()
|
|
||||||
if (name.length > 3 &&
|
|
||||||
((name.startsWith("get") && desc.valueParameters.isEmpty() && desc.returnType != null) ||
|
|
||||||
(name.startsWith("set") && desc.valueParameters.size == 1))) {
|
|
||||||
if (desc in matchedDescriptors) return
|
|
||||||
matchedDescriptors += desc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pFunctions++
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
processed += desc
|
|
||||||
}
|
|
||||||
recursive(desc)
|
|
||||||
val resultDescriptors = mutableSetOf<FunctionDescriptor>()
|
|
||||||
matchedDescriptors.flatMapTo(resultDescriptors) {
|
|
||||||
sequenceOf(it, *(it.overriddenDescriptors.toTypedArray())).asIterable()
|
|
||||||
}
|
|
||||||
println("Found ${resultDescriptors.size} accessors")
|
|
||||||
|
|
||||||
|
|
||||||
fun PsiMethod.isTrivial(): Boolean {
|
|
||||||
val t = this.text
|
|
||||||
val s = t.indexOf('{')
|
|
||||||
val e = t.lastIndexOf('}')
|
|
||||||
return if (s != e && s != -1) t.substring(s, e).lines().size <= 3 else true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val descriptorToPsiBinding = mutableMapOf<FunctionDescriptor, PsiMethod>()
|
|
||||||
|
|
||||||
|
|
||||||
var i = 0
|
|
||||||
resultDescriptors.forEach { desc ->
|
|
||||||
progress("Step 2: ${i++} of ${resultDescriptors.size}", "$desc")
|
|
||||||
val source = DescriptorToSourceUtilsIde.getAllDeclarations(project, desc)
|
|
||||||
.filterIsInstance<PsiMethod>()
|
|
||||||
.firstOrNull() ?: return@forEach
|
|
||||||
val abstract = source.modifierList.hasModifierProperty(PsiModifier.ABSTRACT)
|
|
||||||
if (!abstract) {
|
|
||||||
if (!source.isTrivial()) {
|
|
||||||
descriptorToPsiBinding[desc] = source
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val nonTrivial = mutableSetOf<FunctionDescriptor>()
|
|
||||||
i = 0
|
|
||||||
descriptorToPsiBinding.forEach { t, u ->
|
|
||||||
progress("Step 3: ${i++} of ${descriptorToPsiBinding.size}", "$t")
|
|
||||||
val descriptors = t.overriddenDescriptors
|
|
||||||
var impl = false
|
|
||||||
descriptors.forEach {
|
|
||||||
val source = DescriptorToSourceUtilsIde.getAllDeclarations(project, it).filterIsInstance<PsiMethod>().firstOrNull()
|
|
||||||
if (source != null) {
|
|
||||||
if (source.body != null || source.hasModifierProperty(PsiModifier.ABSTRACT))
|
|
||||||
nonTrivial += it
|
|
||||||
impl = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (u.body != null)
|
|
||||||
if (!impl)
|
|
||||||
nonTrivial += t
|
|
||||||
}
|
|
||||||
nonTrivial.forEach(::println)
|
|
||||||
println("Non trivial count: ${nonTrivial.size}")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun update(e: AnActionEvent) {
|
|
||||||
if (!KotlinInternalMode.enabled) {
|
|
||||||
e.presentation.isVisible = false
|
|
||||||
e.presentation.isEnabled = false
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
e.presentation.isVisible = true
|
|
||||||
e.presentation.isEnabled = e.getData(CommonDataKeys.PSI_FILE) is KtFile
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
+2
-1
@@ -41,6 +41,7 @@ import org.jetbrains.kotlin.idea.references.KtSimpleReference
|
|||||||
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
|
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
|
||||||
import org.jetbrains.kotlin.idea.search.usagesSearch.searchReferencesOrMethodReferences
|
import org.jetbrains.kotlin.idea.search.usagesSearch.searchReferencesOrMethodReferences
|
||||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.progressIndicatorNullable
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
||||||
@@ -186,7 +187,7 @@ class ConvertFunctionTypeParameterToReceiverIntention : SelfTargetingRangeIntent
|
|||||||
runReadAction {
|
runReadAction {
|
||||||
val progressStep = 1.0/callables.size
|
val progressStep = 1.0/callables.size
|
||||||
for ((i, callable) in callables.withIndex()) {
|
for ((i, callable) in callables.withIndex()) {
|
||||||
ProgressManager.getInstance().progressIndicator.fraction = (i + 1) * progressStep
|
ProgressManager.getInstance().progressIndicatorNullable!!.fraction = (i + 1) * progressStep
|
||||||
|
|
||||||
if (callable !is PsiNamedElement) continue
|
if (callable !is PsiNamedElement) continue
|
||||||
|
|
||||||
|
|||||||
-356
@@ -1,356 +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.intentions
|
|
||||||
|
|
||||||
import com.intellij.openapi.editor.Editor
|
|
||||||
import com.intellij.openapi.progress.ProgressManager
|
|
||||||
import com.intellij.openapi.util.TextRange
|
|
||||||
import com.intellij.openapi.util.text.StringUtil
|
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import com.intellij.psi.PsiNamedElement
|
|
||||||
import com.intellij.psi.search.LocalSearchScope
|
|
||||||
import com.intellij.psi.search.searches.ReferencesSearch
|
|
||||||
import com.intellij.refactoring.util.RefactoringUIUtil
|
|
||||||
import com.intellij.util.containers.MultiMap
|
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
|
||||||
import org.jetbrains.kotlin.idea.core.*
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.CallableRefactoring
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.explicateReceiverOf
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.getAffectedCallables
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinIntroduceVariableHandler
|
|
||||||
import org.jetbrains.kotlin.idea.references.KtReference
|
|
||||||
import org.jetbrains.kotlin.idea.references.KtSimpleReference
|
|
||||||
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
|
|
||||||
import org.jetbrains.kotlin.idea.search.usagesSearch.searchReferencesOrMethodReferences
|
|
||||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
|
||||||
import org.jetbrains.kotlin.psi.*
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
|
|
||||||
import org.jetbrains.kotlin.psi.typeRefHelpers.setReceiverTypeReference
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
|
||||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getAbbreviatedTypeOrType
|
|
||||||
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getArgumentByParameterIndex
|
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
|
||||||
|
|
||||||
class ConvertFunctionTypeParameterToReceiverIntention : SelfTargetingRangeIntention<KtTypeReference>(
|
|
||||||
KtTypeReference::class.java,
|
|
||||||
"Convert function type parameter to receiver"
|
|
||||||
) {
|
|
||||||
class FunctionDefinitionInfo(element: KtFunction) : AbstractProcessableUsageInfo<KtFunction, ConversionData>(element) {
|
|
||||||
override fun process(data: ConversionData, elementsToShorten: MutableList<KtElement>) {
|
|
||||||
val function = element ?: return
|
|
||||||
val functionParameter = function.valueParameters.getOrNull(data.functionParameterIndex) ?: return
|
|
||||||
val functionType = functionParameter.typeReference?.typeElement as? KtFunctionType ?: return
|
|
||||||
val functionTypeParameterList = functionType.parameterList ?: return
|
|
||||||
val parameterToMove = functionTypeParameterList.parameters.getOrNull(data.typeParameterIndex) ?: return
|
|
||||||
val typeReferenceToMove = parameterToMove.typeReference ?: return
|
|
||||||
functionType.setReceiverTypeReference(typeReferenceToMove)
|
|
||||||
functionTypeParameterList.removeParameter(parameterToMove)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ParameterCallInfo(element: KtCallExpression) : AbstractProcessableUsageInfo<KtCallExpression, ConversionData>(element) {
|
|
||||||
override fun process(data: ConversionData, elementsToShorten: MutableList<KtElement>) {
|
|
||||||
val callExpression = element ?: return
|
|
||||||
val argumentList = callExpression.valueArgumentList ?: return
|
|
||||||
val expressionToMove = argumentList.arguments.getOrNull(data.typeParameterIndex)?.getArgumentExpression() ?: return
|
|
||||||
val callWithReceiver = KtPsiFactory(callExpression).createExpressionByPattern("$0.$1", expressionToMove, callExpression) as KtQualifiedExpression
|
|
||||||
(callWithReceiver.selectorExpression as KtCallExpression).valueArgumentList!!.removeArgument(data.typeParameterIndex)
|
|
||||||
callExpression.replace(callWithReceiver)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class InternalReferencePassInfo(element: KtSimpleNameExpression) : AbstractProcessableUsageInfo<KtSimpleNameExpression, ConversionData>(element) {
|
|
||||||
override fun process(data: ConversionData, elementsToShorten: MutableList<KtElement>) {
|
|
||||||
val expression = element ?: return
|
|
||||||
val lambdaType = data.lambdaType
|
|
||||||
val validator = CollectingNameValidator()
|
|
||||||
val parameterNames = lambdaType.arguments
|
|
||||||
.dropLast(1)
|
|
||||||
.map { KotlinNameSuggester.suggestNamesByType(it.type, validator, "p").first() }
|
|
||||||
val receiver = parameterNames.getOrNull(data.typeParameterIndex) ?: return
|
|
||||||
val arguments = parameterNames.filter { it != receiver }
|
|
||||||
val adapterLambda = KtPsiFactory(expression).createLambdaExpression(
|
|
||||||
parameterNames.joinToString(),
|
|
||||||
"$receiver.${expression.text}(${arguments.joinToString()})"
|
|
||||||
)
|
|
||||||
expression.replaced(adapterLambda).let {
|
|
||||||
MoveLambdaOutsideParenthesesIntention.moveFunctionLiteralOutsideParenthesesIfPossible(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LambdaInfo(element: KtExpression) : AbstractProcessableUsageInfo<KtExpression, ConversionData>(element) {
|
|
||||||
override fun process(data: ConversionData, elementsToShorten: MutableList<KtElement>) {
|
|
||||||
val expression = element ?: return
|
|
||||||
val context = expression.analyze(BodyResolveMode.PARTIAL)
|
|
||||||
val psiFactory = KtPsiFactory(expression)
|
|
||||||
|
|
||||||
if (expression is KtLambdaExpression || (expression !is KtSimpleNameExpression && expression !is KtCallableReferenceExpression)) {
|
|
||||||
expression.forEachDescendantOfType<KtThisExpression> {
|
|
||||||
if (it.getLabelName() != null) return@forEachDescendantOfType
|
|
||||||
val descriptor = context[BindingContext.REFERENCE_TARGET, it.instanceReference] ?: return@forEachDescendantOfType
|
|
||||||
it.replace(psiFactory.createExpression(explicateReceiverOf(descriptor)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (expression is KtLambdaExpression) {
|
|
||||||
expression.valueParameters.getOrNull(data.typeParameterIndex)?.let { parameterToConvert ->
|
|
||||||
val thisRefExpr = psiFactory.createThisExpression()
|
|
||||||
for (ref in ReferencesSearch.search(parameterToConvert, LocalSearchScope(expression))) {
|
|
||||||
(ref.element as? KtSimpleNameExpression)?.replace(thisRefExpr)
|
|
||||||
}
|
|
||||||
val lambda = expression.functionLiteral
|
|
||||||
lambda.valueParameterList!!.removeParameter(parameterToConvert)
|
|
||||||
if (lambda.valueParameters.isEmpty()) {
|
|
||||||
lambda.arrow?.delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val originalLambdaTypes = data.lambdaType
|
|
||||||
val originalParameterTypes = originalLambdaTypes.arguments.dropLast(1).map { it.type }
|
|
||||||
|
|
||||||
val calleeText = when (expression) {
|
|
||||||
is KtSimpleNameExpression -> expression.text
|
|
||||||
is KtCallableReferenceExpression -> "(${expression.text})"
|
|
||||||
else -> generateVariable(expression)
|
|
||||||
}
|
|
||||||
|
|
||||||
val parameterNameValidator = CollectingNameValidator(
|
|
||||||
if (expression !is KtCallableReferenceExpression) listOf(calleeText) else emptyList()
|
|
||||||
)
|
|
||||||
val parameterNamesWithReceiver = originalParameterTypes.mapIndexed { i, type ->
|
|
||||||
if (i != data.typeParameterIndex) KotlinNameSuggester.suggestNamesByType(type, parameterNameValidator, "p").first() else "this"
|
|
||||||
}
|
|
||||||
val parameterNames = parameterNamesWithReceiver.filter { it != "this" }
|
|
||||||
|
|
||||||
val body = psiFactory.createExpression(parameterNamesWithReceiver.joinToString(prefix = "$calleeText(", postfix = ")"))
|
|
||||||
|
|
||||||
val replacingLambda = psiFactory.buildExpression {
|
|
||||||
appendFixedText("{ ")
|
|
||||||
appendFixedText(parameterNames.joinToString())
|
|
||||||
appendFixedText(" -> ")
|
|
||||||
appendExpression(body)
|
|
||||||
appendFixedText(" }")
|
|
||||||
} as KtLambdaExpression
|
|
||||||
|
|
||||||
expression.replaced(replacingLambda).let {
|
|
||||||
MoveLambdaOutsideParenthesesIntention.moveFunctionLiteralOutsideParenthesesIfPossible(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateVariable(expression: KtExpression): String {
|
|
||||||
var baseCallee = ""
|
|
||||||
KotlinIntroduceVariableHandler.doRefactoring(project, null, expression, false, emptyList()) {
|
|
||||||
baseCallee = it.name!!
|
|
||||||
}
|
|
||||||
return baseCallee
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class Converter(
|
|
||||||
private val data: ConversionData
|
|
||||||
) : CallableRefactoring<CallableDescriptor>(data.function.project, data.functionDescriptor, text) {
|
|
||||||
override fun performRefactoring(descriptorsForChange: Collection<CallableDescriptor>) {
|
|
||||||
val callables = getAffectedCallables(project, descriptorsForChange)
|
|
||||||
|
|
||||||
val conflicts = MultiMap<PsiElement, String>()
|
|
||||||
|
|
||||||
val usages = ArrayList<AbstractProcessableUsageInfo<*, ConversionData>>()
|
|
||||||
|
|
||||||
project.runSynchronouslyWithProgress("Looking for usages and conflicts...", true) {
|
|
||||||
runReadAction {
|
|
||||||
val progressStep = 1.0/callables.size
|
|
||||||
for ((i, callable) in callables.withIndex()) {
|
|
||||||
ProgressManager.getInstance().progressIndicator!!.fraction = (i + 1) * progressStep
|
|
||||||
|
|
||||||
if (callable !is PsiNamedElement) continue
|
|
||||||
|
|
||||||
if (!checkModifiable(callable)) {
|
|
||||||
val renderedCallable = RefactoringUIUtil.getDescription(callable, true).capitalize()
|
|
||||||
conflicts.putValue(callable, "Can't modify $renderedCallable")
|
|
||||||
}
|
|
||||||
|
|
||||||
usageLoop@ for (ref in callable.searchReferencesOrMethodReferences()) {
|
|
||||||
val refElement = ref.element ?: continue
|
|
||||||
when (ref) {
|
|
||||||
is KtSimpleReference<*> -> processExternalUsage(conflicts, refElement, usages)
|
|
||||||
is KtReference -> continue@usageLoop
|
|
||||||
else -> {
|
|
||||||
if (data.isFirstParameter) continue@usageLoop
|
|
||||||
conflicts.putValue(
|
|
||||||
refElement,
|
|
||||||
"Can't replace non-Kotlin reference with call expression: " + StringUtil.htmlEmphasize(refElement.text)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (callable is KtFunction) {
|
|
||||||
usages += FunctionDefinitionInfo(callable)
|
|
||||||
processInternalUsages(callable, usages)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
project.checkConflictsInteractively(conflicts) {
|
|
||||||
project.executeWriteCommand(text) {
|
|
||||||
val elementsToShorten = ArrayList<KtElement>()
|
|
||||||
usages.forEach { it.process(data, elementsToShorten) }
|
|
||||||
ShortenReferences.DEFAULT.process(elementsToShorten)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun processExternalUsage(
|
|
||||||
conflicts: MultiMap<PsiElement, String>,
|
|
||||||
refElement: PsiElement,
|
|
||||||
usages: ArrayList<AbstractProcessableUsageInfo<*, ConversionData>>
|
|
||||||
) {
|
|
||||||
val callElement = refElement.getParentOfTypeAndBranch<KtCallElement> { calleeExpression }
|
|
||||||
if (callElement != null) {
|
|
||||||
val context = callElement.analyze(BodyResolveMode.PARTIAL)
|
|
||||||
val expressionToProcess = getArgumentExpressionToProcess(callElement, context) ?: return
|
|
||||||
|
|
||||||
if (!data.isFirstParameter
|
|
||||||
&& callElement is KtConstructorDelegationCall
|
|
||||||
&& expressionToProcess !is KtLambdaExpression
|
|
||||||
&& expressionToProcess !is KtSimpleNameExpression
|
|
||||||
&& expressionToProcess !is KtCallableReferenceExpression) {
|
|
||||||
conflicts.putValue(
|
|
||||||
expressionToProcess,
|
|
||||||
"Following expression won't be processed since refactoring can't preserve its semantics: ${expressionToProcess.text}"
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!checkThisExpressionsAreExplicatable(conflicts, context, expressionToProcess)) return
|
|
||||||
|
|
||||||
if (data.isFirstParameter && expressionToProcess !is KtLambdaExpression) return
|
|
||||||
|
|
||||||
usages += LambdaInfo(expressionToProcess)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.isFirstParameter) return
|
|
||||||
|
|
||||||
val callableReference = refElement.getParentOfTypeAndBranch<KtCallableReferenceExpression> { callableReference }
|
|
||||||
if (callableReference != null) {
|
|
||||||
conflicts.putValue(
|
|
||||||
refElement,
|
|
||||||
"Callable reference transformation is not supported: " + StringUtil.htmlEmphasize(callableReference.text)
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getArgumentExpressionToProcess(callElement: KtCallElement, context: BindingContext): KtExpression? {
|
|
||||||
return callElement
|
|
||||||
.getArgumentByParameterIndex(data.functionParameterIndex, context)
|
|
||||||
.singleOrNull()
|
|
||||||
?.getArgumentExpression()
|
|
||||||
?.let { KtPsiUtil.safeDeparenthesize(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkThisExpressionsAreExplicatable(conflicts: MultiMap<PsiElement, String>, context: BindingContext, expressionToProcess: KtExpression): Boolean {
|
|
||||||
for (thisExpr in expressionToProcess.collectDescendantsOfType<KtThisExpression>()) {
|
|
||||||
if (thisExpr.getLabelName() != null) continue
|
|
||||||
val descriptor = context[BindingContext.REFERENCE_TARGET, thisExpr.instanceReference] ?: continue
|
|
||||||
if (explicateReceiverOf(descriptor) == "this") {
|
|
||||||
conflicts.putValue(
|
|
||||||
thisExpr,
|
|
||||||
"Following expression won't be processed since refactoring can't preserve its semantics: ${thisExpr.text}"
|
|
||||||
)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun processInternalUsages(callable: KtFunction, usages: ArrayList<AbstractProcessableUsageInfo<*, ConversionData>>) {
|
|
||||||
val body = when (callable) {
|
|
||||||
is KtConstructor<*> -> callable.containingClassOrObject?.getBody()
|
|
||||||
else -> callable.bodyExpression
|
|
||||||
}
|
|
||||||
if (body != null) {
|
|
||||||
val functionParameter = callable.valueParameters.getOrNull(data.functionParameterIndex) ?: return
|
|
||||||
for (ref in ReferencesSearch.search(functionParameter, LocalSearchScope(body))) {
|
|
||||||
val element = ref.element as? KtSimpleNameExpression ?: continue
|
|
||||||
val callExpression = element.getParentOfTypeAndBranch<KtCallExpression> { calleeExpression }
|
|
||||||
if (callExpression != null) {
|
|
||||||
usages += ParameterCallInfo(callExpression)
|
|
||||||
}
|
|
||||||
else if (!data.isFirstParameter) {
|
|
||||||
usages += InternalReferencePassInfo(element)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ConversionData(
|
|
||||||
val typeParameterIndex: Int,
|
|
||||||
val functionParameterIndex: Int,
|
|
||||||
val lambdaType: KotlinType,
|
|
||||||
val function: KtFunction
|
|
||||||
) {
|
|
||||||
val isFirstParameter: Boolean get() = typeParameterIndex == 0
|
|
||||||
val functionDescriptor by lazy { function.unsafeResolveToDescriptor() as FunctionDescriptor }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun KtTypeReference.getConversionData(): ConversionData? {
|
|
||||||
val parameter = parent as? KtParameter ?: return null
|
|
||||||
val functionType = parameter.getParentOfTypeAndBranch<KtFunctionType> { parameterList } ?: return null
|
|
||||||
if (functionType.receiverTypeReference != null) return null
|
|
||||||
val lambdaType = functionType.getAbbreviatedTypeOrType(functionType.analyze(BodyResolveMode.PARTIAL)) ?: return null
|
|
||||||
val containingParameter = (functionType.parent as? KtTypeReference)?.parent as? KtParameter ?: return null
|
|
||||||
val ownerFunction = containingParameter.ownerFunction as? KtFunction ?: return null
|
|
||||||
val typeParameterIndex = functionType.parameters.indexOf(parameter)
|
|
||||||
val functionParameterIndex = ownerFunction.valueParameters.indexOf(containingParameter)
|
|
||||||
return ConversionData(typeParameterIndex, functionParameterIndex, lambdaType, ownerFunction)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun startInWriteAction(): Boolean = false
|
|
||||||
|
|
||||||
override fun applicabilityRange(element: KtTypeReference): TextRange? {
|
|
||||||
val data = element.getConversionData() ?: return null
|
|
||||||
|
|
||||||
val elementBefore = data.function.valueParameters[data.functionParameterIndex].typeReference!!.typeElement as KtFunctionType
|
|
||||||
val elementAfter = elementBefore.copied().apply {
|
|
||||||
setReceiverTypeReference(element)
|
|
||||||
parameterList!!.removeParameter(data.typeParameterIndex)
|
|
||||||
}
|
|
||||||
text = "Convert '${elementBefore.text}' to '${elementAfter.text}'"
|
|
||||||
|
|
||||||
return element.textRange
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun applyTo(element: KtTypeReference, editor: Editor?) {
|
|
||||||
element.getConversionData()?.let { Converter(it).run() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-1
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.idea.references.KtSimpleReference
|
|||||||
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
|
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
|
||||||
import org.jetbrains.kotlin.idea.search.usagesSearch.searchReferencesOrMethodReferences
|
import org.jetbrains.kotlin.idea.search.usagesSearch.searchReferencesOrMethodReferences
|
||||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.progressIndicatorNullable
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||||
@@ -125,7 +126,7 @@ class ConvertFunctionTypeReceiverToParameterIntention : SelfTargetingRangeIntent
|
|||||||
runReadAction {
|
runReadAction {
|
||||||
val progressStep = 1.0/callables.size
|
val progressStep = 1.0/callables.size
|
||||||
for ((i, callable) in callables.withIndex()) {
|
for ((i, callable) in callables.withIndex()) {
|
||||||
ProgressManager.getInstance().progressIndicator.fraction = (i + 1) * progressStep
|
ProgressManager.getInstance().progressIndicatorNullable!!.fraction = (i + 1) * progressStep
|
||||||
|
|
||||||
if (callable !is KtFunction) continue
|
if (callable !is KtFunction) continue
|
||||||
|
|
||||||
|
|||||||
-222
@@ -1,222 +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.intentions
|
|
||||||
|
|
||||||
import com.intellij.openapi.editor.Editor
|
|
||||||
import com.intellij.openapi.progress.ProgressManager
|
|
||||||
import com.intellij.openapi.util.TextRange
|
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import com.intellij.psi.search.LocalSearchScope
|
|
||||||
import com.intellij.psi.search.searches.ReferencesSearch
|
|
||||||
import com.intellij.refactoring.util.RefactoringUIUtil
|
|
||||||
import com.intellij.util.containers.MultiMap
|
|
||||||
import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType
|
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
|
||||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
|
||||||
import org.jetbrains.kotlin.idea.core.*
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.CallableRefactoring
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.getAffectedCallables
|
|
||||||
import org.jetbrains.kotlin.idea.references.KtSimpleReference
|
|
||||||
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
|
|
||||||
import org.jetbrains.kotlin.idea.search.usagesSearch.searchReferencesOrMethodReferences
|
|
||||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
|
||||||
import org.jetbrains.kotlin.psi.*
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
|
||||||
import org.jetbrains.kotlin.psi.typeRefHelpers.setReceiverTypeReference
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
|
||||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getAbbreviatedTypeOrType
|
|
||||||
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getArgumentByParameterIndex
|
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
|
||||||
|
|
||||||
class ConvertFunctionTypeReceiverToParameterIntention : SelfTargetingRangeIntention<KtTypeReference>(
|
|
||||||
KtTypeReference::class.java,
|
|
||||||
"Convert function type receiver to parameter"
|
|
||||||
) {
|
|
||||||
class ConversionData(
|
|
||||||
val functionParameterIndex: Int,
|
|
||||||
val lambdaReceiverType: KotlinType,
|
|
||||||
val function: KtFunction
|
|
||||||
) {
|
|
||||||
val functionDescriptor by lazy { function.unsafeResolveToDescriptor() as FunctionDescriptor }
|
|
||||||
}
|
|
||||||
|
|
||||||
class FunctionDefinitionInfo(element: KtFunction) : AbstractProcessableUsageInfo<KtFunction, ConversionData>(element) {
|
|
||||||
override fun process(data: ConversionData, elementsToShorten: MutableList<KtElement>) {
|
|
||||||
val function = element ?: return
|
|
||||||
val functionParameter = function.valueParameters.getOrNull(data.functionParameterIndex) ?: return
|
|
||||||
val functionType = functionParameter.typeReference?.typeElement as? KtFunctionType ?: return
|
|
||||||
val functionTypeParameterList = functionType.parameterList ?: return
|
|
||||||
val functionTypeReceiver = functionType.receiverTypeReference ?: return
|
|
||||||
val parameterToAdd = KtPsiFactory(project).createFunctionTypeParameter(functionTypeReceiver)
|
|
||||||
functionTypeParameterList.addParameterBefore(parameterToAdd, functionTypeParameterList.parameters.firstOrNull())
|
|
||||||
functionType.setReceiverTypeReference(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ParameterCallInfo(element: KtCallExpression) : AbstractProcessableUsageInfo<KtCallExpression, ConversionData>(element) {
|
|
||||||
override fun process(data: ConversionData, elementsToShorten: MutableList<KtElement>) {
|
|
||||||
val callExpression = element ?: return
|
|
||||||
val qualifiedExpression = callExpression.getQualifiedExpressionForSelector() ?: return
|
|
||||||
val receiverExpression = qualifiedExpression.receiverExpression
|
|
||||||
val argumentList = callExpression.getOrCreateValueArgumentList()
|
|
||||||
argumentList.addArgumentBefore(KtPsiFactory(project).createArgument(receiverExpression), argumentList.arguments.firstOrNull())
|
|
||||||
qualifiedExpression.replace(callExpression)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LambdaInfo(element: KtLambdaExpression) : AbstractProcessableUsageInfo<KtLambdaExpression, ConversionData>(element) {
|
|
||||||
override fun process(data: ConversionData, elementsToShorten: MutableList<KtElement>) {
|
|
||||||
val lambda = element?.functionLiteral ?: return
|
|
||||||
val context = lambda.analyze(BodyResolveMode.PARTIAL)
|
|
||||||
|
|
||||||
val psiFactory = KtPsiFactory(project)
|
|
||||||
|
|
||||||
val validator = CollectingNameValidator(
|
|
||||||
lambda.valueParameters.mapNotNull { it.name },
|
|
||||||
NewDeclarationNameValidator(lambda.bodyExpression!!, null, NewDeclarationNameValidator.Target.VARIABLES)
|
|
||||||
)
|
|
||||||
val newParameterName = KotlinNameSuggester.suggestNamesByType(data.lambdaReceiverType, validator, "p").first()
|
|
||||||
val newParameterRefExpression = psiFactory.createExpression(newParameterName)
|
|
||||||
|
|
||||||
lambda.forEachDescendantOfType<KtThisExpression> {
|
|
||||||
val thisTarget = context[BindingContext.REFERENCE_TARGET, it.instanceReference] ?: return@forEachDescendantOfType
|
|
||||||
if (DescriptorToSourceUtilsIde.getAnyDeclaration(project, thisTarget) == lambda) {
|
|
||||||
it.replace(newParameterRefExpression)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val lambdaParameterList = lambda.getOrCreateParameterList()
|
|
||||||
val parameterToAdd = psiFactory.createLambdaParameterList(newParameterName).parameters.first()
|
|
||||||
lambdaParameterList.addParameterBefore(parameterToAdd, lambdaParameterList.parameters.firstOrNull())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class Converter(
|
|
||||||
private val data: ConversionData
|
|
||||||
) : CallableRefactoring<CallableDescriptor>(data.function.project, data.functionDescriptor, text) {
|
|
||||||
override fun performRefactoring(descriptorsForChange: Collection<CallableDescriptor>) {
|
|
||||||
val callables = getAffectedCallables(project, descriptorsForChange)
|
|
||||||
|
|
||||||
val conflicts = MultiMap<PsiElement, String>()
|
|
||||||
|
|
||||||
val usages = ArrayList<AbstractProcessableUsageInfo<*, ConversionData>>()
|
|
||||||
|
|
||||||
project.runSynchronouslyWithProgress("Looking for usages and conflicts...", true) {
|
|
||||||
runReadAction {
|
|
||||||
val progressStep = 1.0/callables.size
|
|
||||||
for ((i, callable) in callables.withIndex()) {
|
|
||||||
ProgressManager.getInstance().progressIndicator!!.fraction = (i + 1) * progressStep
|
|
||||||
|
|
||||||
if (callable !is KtFunction) continue
|
|
||||||
|
|
||||||
if (!checkModifiable(callable)) {
|
|
||||||
val renderedCallable = RefactoringUIUtil.getDescription(callable, true).capitalize()
|
|
||||||
conflicts.putValue(callable, "Can't modify $renderedCallable")
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ref in callable.searchReferencesOrMethodReferences()) {
|
|
||||||
if (ref !is KtSimpleReference<*>) continue
|
|
||||||
processExternalUsage(ref, usages)
|
|
||||||
}
|
|
||||||
|
|
||||||
usages += FunctionDefinitionInfo(callable)
|
|
||||||
|
|
||||||
processInternalUsages(callable, usages)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
project.checkConflictsInteractively(conflicts) {
|
|
||||||
project.executeWriteCommand(text) {
|
|
||||||
val elementsToShorten = ArrayList<KtElement>()
|
|
||||||
usages.forEach { it.process(data, elementsToShorten) }
|
|
||||||
ShortenReferences.DEFAULT.process(elementsToShorten)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun processExternalUsage(ref: KtSimpleReference<*>, usages: java.util.ArrayList<AbstractProcessableUsageInfo<*, ConversionData>>) {
|
|
||||||
val callElement = ref.element.getParentOfTypeAndBranch<KtCallElement> { calleeExpression } ?: return
|
|
||||||
val context = callElement.analyze(BodyResolveMode.PARTIAL)
|
|
||||||
val expressionToProcess = callElement
|
|
||||||
.getArgumentByParameterIndex(data.functionParameterIndex, context)
|
|
||||||
.singleOrNull()
|
|
||||||
?.getArgumentExpression()
|
|
||||||
?.let { KtPsiUtil.safeDeparenthesize(it) }
|
|
||||||
?: return
|
|
||||||
if (expressionToProcess is KtLambdaExpression) {
|
|
||||||
usages += LambdaInfo(expressionToProcess)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun processInternalUsages(callable: KtFunction, usages: java.util.ArrayList<AbstractProcessableUsageInfo<*, ConversionData>>) {
|
|
||||||
val body = when (callable) {
|
|
||||||
is KtConstructor<*> -> callable.containingClassOrObject?.getBody()
|
|
||||||
else -> callable.bodyExpression
|
|
||||||
}
|
|
||||||
if (body != null) {
|
|
||||||
val functionParameter = callable.valueParameters.getOrNull(data.functionParameterIndex) ?: return
|
|
||||||
for (ref in ReferencesSearch.search(functionParameter, LocalSearchScope(body))) {
|
|
||||||
val element = ref.element as? KtSimpleNameExpression ?: continue
|
|
||||||
val callExpression = element.getParentOfTypeAndBranch<KtCallExpression> { calleeExpression } ?: continue
|
|
||||||
usages += ParameterCallInfo(callExpression)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun KtTypeReference.getConversionData(): ConversionData? {
|
|
||||||
val functionTypeReceiver = parent as? KtFunctionTypeReceiver ?: return null
|
|
||||||
val functionType = functionTypeReceiver.parent as? KtFunctionType ?: return null
|
|
||||||
val lambdaReceiverType = functionType
|
|
||||||
.getAbbreviatedTypeOrType(functionType.analyze(BodyResolveMode.PARTIAL))
|
|
||||||
?.getReceiverTypeFromFunctionType()
|
|
||||||
?: return null
|
|
||||||
val containingParameter = (functionType.parent as? KtTypeReference)?.parent as? KtParameter ?: return null
|
|
||||||
val ownerFunction = containingParameter.ownerFunction as? KtFunction ?: return null
|
|
||||||
val functionParameterIndex = ownerFunction.valueParameters.indexOf(containingParameter)
|
|
||||||
return ConversionData(functionParameterIndex, lambdaReceiverType, ownerFunction)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun startInWriteAction(): Boolean = false
|
|
||||||
|
|
||||||
override fun applicabilityRange(element: KtTypeReference): TextRange? {
|
|
||||||
val data = element.getConversionData() ?: return null
|
|
||||||
|
|
||||||
val elementBefore = data.function.valueParameters[data.functionParameterIndex].typeReference!!.typeElement as KtFunctionType
|
|
||||||
val elementAfter = elementBefore.copied().apply {
|
|
||||||
parameterList!!.addParameterBefore(
|
|
||||||
KtPsiFactory(element).createFunctionTypeParameter(element),
|
|
||||||
parameterList!!.parameters.firstOrNull()
|
|
||||||
)
|
|
||||||
setReceiverTypeReference(null)
|
|
||||||
}
|
|
||||||
text = "Convert '${elementBefore.text}' to '${elementAfter.text}'"
|
|
||||||
|
|
||||||
return element.textRange
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun applyTo(element: KtTypeReference, editor: Editor?) {
|
|
||||||
element.getConversionData()?.let { Converter(it).run() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.idea.references.KtReference
|
|||||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||||
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
|
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
|
||||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.progressIndicatorNullable
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||||
@@ -97,7 +98,7 @@ class ConvertPropertyToFunctionIntention : SelfTargetingIntention<KtProperty>(Kt
|
|||||||
runReadAction {
|
runReadAction {
|
||||||
val progressStep = 1.0/callables.size
|
val progressStep = 1.0/callables.size
|
||||||
for ((i, callable) in callables.withIndex()) {
|
for ((i, callable) in callables.withIndex()) {
|
||||||
ProgressManager.getInstance().progressIndicator.fraction = (i + 1)*progressStep
|
ProgressManager.getInstance().progressIndicatorNullable!!.fraction = (i + 1)*progressStep
|
||||||
|
|
||||||
if (callable !is PsiNamedElement) continue
|
if (callable !is PsiNamedElement) continue
|
||||||
|
|
||||||
|
|||||||
-201
@@ -1,201 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2015 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.intentions
|
|
||||||
|
|
||||||
import com.intellij.codeInsight.intention.LowPriorityAction
|
|
||||||
import com.intellij.openapi.editor.Editor
|
|
||||||
import com.intellij.openapi.progress.ProgressManager
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.openapi.util.text.StringUtil
|
|
||||||
import com.intellij.psi.*
|
|
||||||
import com.intellij.psi.search.searches.ReferencesSearch
|
|
||||||
import com.intellij.refactoring.util.RefactoringUIUtil
|
|
||||||
import com.intellij.util.containers.MultiMap
|
|
||||||
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
|
||||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.*
|
|
||||||
import org.jetbrains.kotlin.idea.references.KtReference
|
|
||||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
|
||||||
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
|
|
||||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
|
||||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
|
||||||
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
|
|
||||||
import org.jetbrains.kotlin.psi.KtProperty
|
|
||||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
|
||||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.siblings
|
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
|
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
|
||||||
import org.jetbrains.kotlin.resolve.scopes.utils.findFunction
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class ConvertPropertyToFunctionIntention : SelfTargetingIntention<KtProperty>(KtProperty::class.java, "Convert property to function"), LowPriorityAction {
|
|
||||||
private inner class Converter(
|
|
||||||
project: Project,
|
|
||||||
descriptor: CallableDescriptor
|
|
||||||
): CallableRefactoring<CallableDescriptor>(project, descriptor, text) {
|
|
||||||
private val newName: String = JvmAbi.getterName(callableDescriptor.name.asString())
|
|
||||||
|
|
||||||
private fun convertProperty(originalProperty: KtProperty, psiFactory: KtPsiFactory) {
|
|
||||||
val property = originalProperty.copy() as KtProperty
|
|
||||||
val getter = property.getter
|
|
||||||
|
|
||||||
val sampleFunction = psiFactory.createFunction("fun foo() {\n\n}")
|
|
||||||
|
|
||||||
property.valOrVarKeyword.replace(sampleFunction.funKeyword!!)
|
|
||||||
property.addAfter(psiFactory.createParameterList("()"), property.nameIdentifier)
|
|
||||||
if (property.initializer == null) {
|
|
||||||
if (getter != null) {
|
|
||||||
val dropGetterTo = (getter.equalsToken ?: getter.bodyExpression)
|
|
||||||
?.siblings(forward = false, withItself = false)
|
|
||||||
?.firstOrNull { it !is PsiWhiteSpace }
|
|
||||||
getter.deleteChildRange(getter.firstChild, dropGetterTo)
|
|
||||||
|
|
||||||
val dropPropertyFrom = getter
|
|
||||||
.siblings(forward = false, withItself = false)
|
|
||||||
.first { it !is PsiWhiteSpace }
|
|
||||||
.nextSibling
|
|
||||||
property.deleteChildRange(dropPropertyFrom, getter.prevSibling)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
property.setName(newName)
|
|
||||||
|
|
||||||
originalProperty.replace(psiFactory.createFunction(property.text))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun performRefactoring(descriptorsForChange: Collection<CallableDescriptor>) {
|
|
||||||
val propertyName = callableDescriptor.name.asString()
|
|
||||||
val nameChanged = propertyName != newName
|
|
||||||
val getterName = JvmAbi.getterName(callableDescriptor.name.asString())
|
|
||||||
val conflicts = MultiMap<PsiElement, String>()
|
|
||||||
val callables = getAffectedCallables(project, descriptorsForChange)
|
|
||||||
val kotlinRefsToReplaceWithCall = ArrayList<KtSimpleNameExpression>()
|
|
||||||
val refsToRename = ArrayList<PsiReference>()
|
|
||||||
val javaRefsToReplaceWithCall = ArrayList<PsiReferenceExpression>()
|
|
||||||
|
|
||||||
project.runSynchronouslyWithProgress("Looking for usages and conflicts...", true) {
|
|
||||||
runReadAction {
|
|
||||||
val progressStep = 1.0/callables.size
|
|
||||||
for ((i, callable) in callables.withIndex()) {
|
|
||||||
ProgressManager.getInstance().progressIndicator!!.fraction = (i + 1) * progressStep
|
|
||||||
|
|
||||||
if (callable !is PsiNamedElement) continue
|
|
||||||
|
|
||||||
if (!checkModifiable(callable)) {
|
|
||||||
val renderedCallable = RefactoringUIUtil.getDescription(callable, true).capitalize()
|
|
||||||
conflicts.putValue(callable, "Can't modify $renderedCallable")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (callable is KtProperty) {
|
|
||||||
callableDescriptor.getContainingScope()
|
|
||||||
?.findFunction(callableDescriptor.name, NoLookupLocation.FROM_IDE) { it.valueParameters.isEmpty() }
|
|
||||||
?.let { DescriptorToSourceUtilsIde.getAnyDeclaration(project, it) }
|
|
||||||
?.let { reportDeclarationConflict(conflicts, it) { "$it already exists" } }
|
|
||||||
}
|
|
||||||
else if (callable is PsiMethod) {
|
|
||||||
callable.containingClass
|
|
||||||
?.findMethodsByName(propertyName, true)
|
|
||||||
// as is necessary here: see KT-10386
|
|
||||||
?.firstOrNull { it.parameterList.parametersCount == 0 && !callables.contains(it.namedUnwrappedElement as PsiElement?) }
|
|
||||||
?.let { reportDeclarationConflict(conflicts, it) { "$it already exists" } }
|
|
||||||
}
|
|
||||||
|
|
||||||
val usages = ReferencesSearch.search(callable)
|
|
||||||
for (usage in usages) {
|
|
||||||
if (usage is KtReference) {
|
|
||||||
if (usage is KtSimpleNameReference) {
|
|
||||||
val expression = usage.expression
|
|
||||||
if (expression.getCall(expression.analyze(BodyResolveMode.PARTIAL)) != null
|
|
||||||
&& expression.getStrictParentOfType<KtCallableReferenceExpression>() == null) {
|
|
||||||
kotlinRefsToReplaceWithCall.add(expression)
|
|
||||||
}
|
|
||||||
else if (nameChanged) {
|
|
||||||
refsToRename.add(usage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val refElement = usage.element
|
|
||||||
conflicts.putValue(
|
|
||||||
refElement,
|
|
||||||
"Unrecognized reference will be skipped: " + StringUtil.htmlEmphasize(refElement.text)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
val refElement = usage.element
|
|
||||||
|
|
||||||
if (refElement.text.endsWith(getterName)) continue
|
|
||||||
|
|
||||||
if (usage is PsiJavaReference) {
|
|
||||||
if (usage.resolve() is PsiField && usage is PsiReferenceExpression) {
|
|
||||||
javaRefsToReplaceWithCall.add(usage)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
conflicts.putValue(
|
|
||||||
refElement,
|
|
||||||
"Can't replace foreign reference with call expression: " + StringUtil.htmlEmphasize(refElement.text)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
project.checkConflictsInteractively(conflicts) {
|
|
||||||
project.executeWriteCommand(text) {
|
|
||||||
val kotlinPsiFactory = KtPsiFactory(project)
|
|
||||||
val javaPsiFactory = PsiElementFactory.SERVICE.getInstance(project)
|
|
||||||
val newKotlinCallExpr = kotlinPsiFactory.createExpression("$newName()")
|
|
||||||
|
|
||||||
kotlinRefsToReplaceWithCall.forEach { it.replace(newKotlinCallExpr) }
|
|
||||||
refsToRename.forEach { it.handleElementRename(newName) }
|
|
||||||
javaRefsToReplaceWithCall.forEach {
|
|
||||||
val getterRef = it.handleElementRename(newName)
|
|
||||||
getterRef.replace(javaPsiFactory.createExpressionFromText("${getterRef.text}()", null))
|
|
||||||
}
|
|
||||||
callables.forEach {
|
|
||||||
when (it) {
|
|
||||||
is KtProperty -> convertProperty(it, kotlinPsiFactory)
|
|
||||||
is PsiMethod -> it.name = newName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun startInWriteAction(): Boolean = false
|
|
||||||
|
|
||||||
override fun isApplicableTo(element: KtProperty, caretOffset: Int): Boolean {
|
|
||||||
val identifier = element.nameIdentifier ?: return false
|
|
||||||
if (!identifier.textRange.containsOffset(caretOffset)) return false
|
|
||||||
return element.delegate == null && !element.isVar && !element.isLocal && (element.initializer == null || element.getter == null)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun applyTo(element: KtProperty, editor: Editor?) {
|
|
||||||
val descriptor = element.resolveToDescriptorIfAny(BodyResolveMode.FULL) as? CallableDescriptor ?: return
|
|
||||||
Converter(element.project, descriptor).run()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user