Convert Receiver to Parameter: Use template instead of the dialog
#KT-9490 Fixed
This commit is contained in:
@@ -103,6 +103,8 @@ These artifacts include extensions for the types available in the latter JDKs, s
|
|||||||
|
|
||||||
#### Intention actions, inspections and quickfixes
|
#### Intention actions, inspections and quickfixes
|
||||||
|
|
||||||
|
- [`KT-9490`](https://youtrack.jetbrains.com/issue/KT-9490) Convert receiver to parameter: use template instead of the dialog
|
||||||
|
|
||||||
##### New features
|
##### New features
|
||||||
|
|
||||||
- [`KT-11525`](https://youtrack.jetbrains.com/issue/KT-11525) Implement "Create type parameter" quickfix
|
- [`KT-11525`](https://youtrack.jetbrains.com/issue/KT-11525) Implement "Create type parameter" quickfix
|
||||||
|
|||||||
@@ -285,4 +285,11 @@ fun KtTypeParameterListOwner.addTypeParameter(typeParameter: KtTypeParameter): K
|
|||||||
else -> null
|
else -> null
|
||||||
} ?: return null
|
} ?: return null
|
||||||
return (addAfter(list, leftAnchor) as KtTypeParameterList).parameters.first()
|
return (addAfter(list, leftAnchor) as KtTypeParameterList).parameters.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtNamedFunction.getOrCreateValueParameterList(): KtParameterList {
|
||||||
|
valueParameterList?.let { return it }
|
||||||
|
val parameterList = KtPsiFactory(this).createParameterList("()")
|
||||||
|
val anchor = nameIdentifier ?: funKeyword!!
|
||||||
|
return addAfter(parameterList, anchor) as KtParameterList
|
||||||
}
|
}
|
||||||
+77
-11
@@ -17,27 +17,43 @@
|
|||||||
package org.jetbrains.kotlin.idea.intentions
|
package org.jetbrains.kotlin.idea.intentions
|
||||||
|
|
||||||
import com.intellij.codeInsight.intention.LowPriorityAction
|
import com.intellij.codeInsight.intention.LowPriorityAction
|
||||||
|
import com.intellij.codeInsight.template.Template
|
||||||
|
import com.intellij.codeInsight.template.TemplateBuilderImpl
|
||||||
|
import com.intellij.codeInsight.template.TemplateEditingAdapter
|
||||||
|
import com.intellij.codeInsight.template.TemplateManager
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.psi.PsiDocumentManager
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinChangeSignatureConfiguration
|
import org.jetbrains.kotlin.idea.core.getOrCreateValueParameterList
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinMethodDescriptor
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.*
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.modify
|
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.runChangeSignature
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||||
|
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||||
import org.jetbrains.kotlin.psi.KtTypeReference
|
import org.jetbrains.kotlin.psi.KtTypeReference
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||||
|
import org.jetbrains.kotlin.psi.typeRefHelpers.setReceiverTypeReference
|
||||||
|
|
||||||
class ConvertReceiverToParameterIntention : SelfTargetingOffsetIndependentIntention<KtTypeReference>(KtTypeReference::class.java, "Convert receiver to parameter"), LowPriorityAction {
|
class ConvertReceiverToParameterIntention : SelfTargetingOffsetIndependentIntention<KtTypeReference>(KtTypeReference::class.java, "Convert receiver to parameter"), LowPriorityAction {
|
||||||
override fun isApplicableTo(element: KtTypeReference): Boolean {
|
override fun isApplicableTo(element: KtTypeReference): Boolean {
|
||||||
return (element.parent as? KtNamedFunction)?.receiverTypeReference == element
|
return (element.parent as? KtNamedFunction)?.receiverTypeReference == element
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun configureChangeSignature(): KotlinChangeSignatureConfiguration {
|
private fun configureChangeSignature(newName: String? = null): KotlinChangeSignatureConfiguration {
|
||||||
return object : KotlinChangeSignatureConfiguration {
|
return object : KotlinChangeSignatureConfiguration {
|
||||||
override fun configure(originalDescriptor: KotlinMethodDescriptor): KotlinMethodDescriptor {
|
override fun configure(originalDescriptor: KotlinMethodDescriptor): KotlinMethodDescriptor {
|
||||||
return originalDescriptor.modify { it.receiver = null }
|
return originalDescriptor.modify {
|
||||||
|
it.receiver = null
|
||||||
|
if (newName != null) {
|
||||||
|
it.parameters.last().name = newName
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun performSilently(affectedFunctions: Collection<PsiElement>) = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,8 +61,58 @@ class ConvertReceiverToParameterIntention : SelfTargetingOffsetIndependentIntent
|
|||||||
|
|
||||||
override fun applyTo(element: KtTypeReference, editor: Editor?) {
|
override fun applyTo(element: KtTypeReference, editor: Editor?) {
|
||||||
val function = element.parent as? KtNamedFunction ?: return
|
val function = element.parent as? KtNamedFunction ?: return
|
||||||
val context = function.analyze()
|
val descriptor = function.resolveToDescriptor() as FunctionDescriptor
|
||||||
val descriptor = context[BindingContext.DECLARATION_TO_DESCRIPTOR, function] as? FunctionDescriptor ?: return
|
|
||||||
runChangeSignature(element.project, descriptor, configureChangeSignature(), element, text)
|
val project = function.project
|
||||||
|
|
||||||
|
if (editor != null && !ApplicationManager.getApplication().isUnitTestMode) {
|
||||||
|
val receiverNames = suggestReceiverNames(project, descriptor)
|
||||||
|
val defaultReceiverName = receiverNames.first()
|
||||||
|
val receiverTypeRef = function.receiverTypeReference!!
|
||||||
|
val psiFactory = KtPsiFactory(element)
|
||||||
|
val newParameter = psiFactory.createParameter("$defaultReceiverName: Dummy").apply { typeReference!!.replace(receiverTypeRef) }
|
||||||
|
|
||||||
|
project.executeWriteCommand(text) {
|
||||||
|
function.setReceiverTypeReference(null)
|
||||||
|
val addedParameter = function.getOrCreateValueParameterList().addParameter(newParameter)
|
||||||
|
|
||||||
|
with(PsiDocumentManager.getInstance(project)) {
|
||||||
|
commitDocument(editor.document)
|
||||||
|
doPostponedOperationsAndUnblockDocument(editor.document)
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.caretModel.moveToOffset(function.startOffset)
|
||||||
|
|
||||||
|
val templateBuilder = TemplateBuilderImpl(function)
|
||||||
|
templateBuilder.replaceElement(addedParameter.nameIdentifier!!, ChooseStringExpression(receiverNames))
|
||||||
|
TemplateManager.getInstance(project).startTemplate(
|
||||||
|
editor,
|
||||||
|
templateBuilder.buildInlineTemplate(),
|
||||||
|
object: TemplateEditingAdapter() {
|
||||||
|
private fun revertChanges() {
|
||||||
|
runWriteAction {
|
||||||
|
function.setReceiverTypeReference(addedParameter.typeReference)
|
||||||
|
function.valueParameterList!!.removeParameter(addedParameter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun templateFinished(template: Template?, brokenOff: Boolean) {
|
||||||
|
val newName = addedParameter.name
|
||||||
|
revertChanges()
|
||||||
|
if (!brokenOff) {
|
||||||
|
runChangeSignature(element.project, descriptor, configureChangeSignature(newName), function.receiverTypeReference!!, text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun templateCancelled(template: Template?) {
|
||||||
|
revertChanges()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
runChangeSignature(element.project, descriptor, configureChangeSignature(), element, text)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-17
@@ -26,23 +26,15 @@ import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
|||||||
import org.jetbrains.kotlin.asJava.toLightMethods
|
import org.jetbrains.kotlin.asJava.toLightMethods
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||||
import org.jetbrains.kotlin.idea.core.CollectingNameValidator
|
|
||||||
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.KotlinCallableDefinitionUsage
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.KotlinCallableDefinitionUsage
|
||||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
|
||||||
import org.jetbrains.kotlin.name.Name
|
|
||||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||||
import org.jetbrains.kotlin.psi.KtClass
|
import org.jetbrains.kotlin.psi.KtClass
|
||||||
import org.jetbrains.kotlin.psi.KtFunction
|
import org.jetbrains.kotlin.psi.KtFunction
|
||||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.scopes.utils.findVariable
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class KotlinChangeSignatureData(
|
class KotlinChangeSignatureData(
|
||||||
@@ -80,16 +72,8 @@ class KotlinChangeSignatureData(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun createReceiverInfoIfNeeded(): KotlinParameterInfo? {
|
private fun createReceiverInfoIfNeeded(): KotlinParameterInfo? {
|
||||||
val callable = baseDeclaration as? KtCallableDeclaration ?: return null
|
|
||||||
val bodyScope = (callable as? KtFunction)?.bodyExpression?.let { it.getResolutionScope(it.analyze(), it.getResolutionFacade()) }
|
|
||||||
val paramNames = baseDescriptor.valueParameters.map { it.name.asString() }
|
|
||||||
val validator = bodyScope?.let { bodyScope ->
|
|
||||||
CollectingNameValidator(paramNames) {
|
|
||||||
bodyScope.findVariable(Name.identifier(it), NoLookupLocation.FROM_IDE) == null
|
|
||||||
}
|
|
||||||
} ?: CollectingNameValidator(paramNames)
|
|
||||||
val receiverType = baseDescriptor.extensionReceiverParameter?.type ?: return null
|
val receiverType = baseDescriptor.extensionReceiverParameter?.type ?: return null
|
||||||
val receiverName = KotlinNameSuggester.suggestNamesByType(receiverType, validator, "receiver").first()
|
val receiverName = suggestReceiverNames(baseDeclaration.project, baseDescriptor).first()
|
||||||
val receiverTypeText = (baseDeclaration as? KtCallableDeclaration)?.receiverTypeReference?.text
|
val receiverTypeText = (baseDeclaration as? KtCallableDeclaration)?.receiverTypeReference?.text
|
||||||
?: IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(receiverType)
|
?: IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(receiverType)
|
||||||
return KotlinParameterInfo(callableDescriptor = baseDescriptor,
|
return KotlinParameterInfo(callableDescriptor = baseDescriptor,
|
||||||
|
|||||||
+24
@@ -16,19 +16,30 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.refactoring.changeSignature
|
package org.jetbrains.kotlin.idea.refactoring.changeSignature
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.refactoring.changeSignature.CallerUsageInfo
|
import com.intellij.refactoring.changeSignature.CallerUsageInfo
|
||||||
import com.intellij.refactoring.changeSignature.OverriderUsageInfo
|
import com.intellij.refactoring.changeSignature.OverriderUsageInfo
|
||||||
import com.intellij.usageView.UsageInfo
|
import com.intellij.usageView.UsageInfo
|
||||||
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
|
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||||
|
import org.jetbrains.kotlin.idea.core.CollectingNameValidator
|
||||||
|
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.DeferredJavaMethodKotlinCallerUsage
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.DeferredJavaMethodKotlinCallerUsage
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.JavaMethodKotlinUsageWithDelegate
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.JavaMethodKotlinUsageWithDelegate
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.KotlinCallableDefinitionUsage
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.KotlinCallableDefinitionUsage
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.KotlinCallerUsage
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.KotlinCallerUsage
|
||||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||||
|
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||||
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
|
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.utils.findVariable
|
||||||
import org.jetbrains.kotlin.types.*
|
import org.jetbrains.kotlin.types.*
|
||||||
import org.jetbrains.kotlin.types.substitutions.getCallableSubstitutor
|
import org.jetbrains.kotlin.types.substitutions.getCallableSubstitutor
|
||||||
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
|
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
|
||||||
@@ -95,3 +106,16 @@ private object ForceTypeCopySubstitution : TypeSubstitution() {
|
|||||||
|
|
||||||
override fun isEmpty() = false
|
override fun isEmpty() = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun suggestReceiverNames(project: Project, descriptor: CallableDescriptor): List<String> {
|
||||||
|
val callable = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor) as? KtCallableDeclaration ?: return emptyList()
|
||||||
|
val bodyScope = (callable as? KtFunction)?.bodyExpression?.let { it.getResolutionScope(it.analyze(), it.getResolutionFacade()) }
|
||||||
|
val paramNames = descriptor.valueParameters.map { it.name.asString() }
|
||||||
|
val validator = bodyScope?.let { bodyScope ->
|
||||||
|
CollectingNameValidator(paramNames) {
|
||||||
|
bodyScope.findVariable(Name.identifier(it), NoLookupLocation.FROM_IDE) == null
|
||||||
|
}
|
||||||
|
} ?: CollectingNameValidator(paramNames)
|
||||||
|
val receiverType = descriptor.extensionReceiverParameter?.type ?: return emptyList()
|
||||||
|
return KotlinNameSuggester.suggestNamesByType(receiverType, validator, "receiver")
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user