Migration to expect/actual: some intentions

This commit is contained in:
Mikhail Glukhikh
2017-09-14 19:43:16 +03:00
parent 62ec4b5a31
commit 280a60af6f
6 changed files with 31 additions and 30 deletions
@@ -20,7 +20,7 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.refactoring.withHeaderImplementations import org.jetbrains.kotlin.idea.refactoring.withExpectedActuals
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.util.OperatorChecks import org.jetbrains.kotlin.util.OperatorChecks
@@ -34,7 +34,7 @@ class AddOperatorModifierIntention : SelfTargetingRangeIntention<KtNamedFunction
} }
override fun applyTo(element: KtNamedFunction, editor: Editor?) { override fun applyTo(element: KtNamedFunction, editor: Editor?) {
element.withHeaderImplementations().forEach { it.addModifier(KtTokens.OPERATOR_KEYWORD) } element.withExpectedActuals().forEach { it.addModifier(KtTokens.OPERATOR_KEYWORD) }
} }
} }
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinChangeSignatu
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinMethodDescriptor import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinMethodDescriptor
import org.jetbrains.kotlin.idea.refactoring.changeSignature.modify import org.jetbrains.kotlin.idea.refactoring.changeSignature.modify
import org.jetbrains.kotlin.idea.refactoring.changeSignature.runChangeSignature import org.jetbrains.kotlin.idea.refactoring.changeSignature.runChangeSignature
import org.jetbrains.kotlin.idea.refactoring.resolveToHeaderDescriptorIfPossible import org.jetbrains.kotlin.idea.refactoring.resolveToExpectedDescriptorIfPossible
import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
@@ -52,7 +52,7 @@ class ConvertParameterToReceiverIntention : SelfTargetingIntention<KtParameter>(
override fun applyTo(element: KtParameter, editor: Editor?) { override fun applyTo(element: KtParameter, editor: Editor?) {
val function = element.getStrictParentOfType<KtNamedFunction>() ?: return val function = element.getStrictParentOfType<KtNamedFunction>() ?: return
val parameterIndex = function.valueParameters.indexOf(element) val parameterIndex = function.valueParameters.indexOf(element)
val descriptor = function.resolveToHeaderDescriptorIfPossible() as? FunctionDescriptor ?: return val descriptor = function.resolveToExpectedDescriptorIfPossible() as? FunctionDescriptor ?: return
runChangeSignature(element.project, descriptor, configureChangeSignature(parameterIndex), element, text) runChangeSignature(element.project, descriptor, configureChangeSignature(parameterIndex), element, text)
} }
} }
@@ -28,7 +28,7 @@ import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.core.getOrCreateValueParameterList import org.jetbrains.kotlin.idea.core.getOrCreateValueParameterList
import org.jetbrains.kotlin.idea.refactoring.changeSignature.* import org.jetbrains.kotlin.idea.refactoring.changeSignature.*
import org.jetbrains.kotlin.idea.refactoring.resolveToHeaderDescriptorIfPossible import org.jetbrains.kotlin.idea.refactoring.resolveToExpectedDescriptorIfPossible
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtNamedFunction
@@ -61,7 +61,7 @@ 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 descriptor = function.resolveToHeaderDescriptorIfPossible() as FunctionDescriptor val descriptor = function.resolveToExpectedDescriptorIfPossible() as FunctionDescriptor
val project = function.project val project = function.project
@@ -102,7 +102,7 @@ class ConvertReceiverToParameterIntention : SelfTargetingOffsetIndependentIntent
if (!brokenOff) { if (!brokenOff) {
runChangeSignature( runChangeSignature(
element.project, element.project,
function.resolveToHeaderDescriptorIfPossible() as FunctionDescriptor, function.resolveToExpectedDescriptorIfPossible() as FunctionDescriptor,
configureChangeSignature(newName), configureChangeSignature(newName),
function.receiverTypeReference!!, function.receiverTypeReference!!,
text text
@@ -38,7 +38,7 @@ import org.jetbrains.kotlin.idea.highlighter.markers.isExpectedOrExpectedClassMe
import org.jetbrains.kotlin.idea.highlighter.markers.liftToExpected import org.jetbrains.kotlin.idea.highlighter.markers.liftToExpected
import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getReturnTypeReference import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getReturnTypeReference
import org.jetbrains.kotlin.idea.refactoring.withHeaderImplementations import org.jetbrains.kotlin.idea.refactoring.withExpectedActuals
import org.jetbrains.kotlin.idea.references.KtReference import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.idea.util.ImportInsertHelper import org.jetbrains.kotlin.idea.util.ImportInsertHelper
import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.idea.util.application.runWriteAction
@@ -61,7 +61,7 @@ class ConvertMemberToExtensionIntention : SelfTargetingRangeIntention<KtCallable
} }
override fun applicabilityRange(element: KtCallableDeclaration): TextRange? { override fun applicabilityRange(element: KtCallableDeclaration): TextRange? {
if (!element.withHeaderImplementations().all { it is KtCallableDeclaration && isApplicable(it) }) return null if (!element.withExpectedActuals().all { it is KtCallableDeclaration && isApplicable(it) }) return null
return (element.nameIdentifier ?: return null).textRange return (element.nameIdentifier ?: return null).textRange
} }
@@ -70,16 +70,16 @@ class ConvertMemberToExtensionIntention : SelfTargetingRangeIntention<KtCallable
//TODO: local class //TODO: local class
override fun applyTo(element: KtCallableDeclaration, editor: Editor?) { override fun applyTo(element: KtCallableDeclaration, editor: Editor?) {
var allowHeader = true var allowExpected = true
element.liftToExpected()?.actualsForExpected()?.let { element.liftToExpected()?.actualsForExpected()?.let {
if (it.isEmpty()) { if (it.isEmpty()) {
allowHeader = askIfHeaderIsAllowed(element.containingKtFile) allowExpected = askIfExpectedIsAllowed(element.containingKtFile)
} }
} }
runWriteAction { runWriteAction {
val (extension, bodyToSelect) = createExtensionCallableAndPrepareBodyToSelect(element, allowHeader) val (extension, bodyToSelect) = createExtensionCallableAndPrepareBodyToSelect(element, allowExpected)
editor?.apply { editor?.apply {
unblockDocument() unblockDocument()
@@ -106,12 +106,12 @@ class ConvertMemberToExtensionIntention : SelfTargetingRangeIntention<KtCallable
private fun processSingleDeclaration( private fun processSingleDeclaration(
element: KtCallableDeclaration, element: KtCallableDeclaration,
allowHeader: Boolean allowExpected: Boolean
): Pair<KtCallableDeclaration, KtExpression?> { ): Pair<KtCallableDeclaration, KtExpression?> {
val descriptor = element.unsafeResolveToDescriptor() val descriptor = element.unsafeResolveToDescriptor()
val containingClass = descriptor.containingDeclaration as ClassDescriptor val containingClass = descriptor.containingDeclaration as ClassDescriptor
val isEffectiveHeader = allowHeader && element.isExpectedOrExpectedClassMember() val isEffectivelyExpected = allowExpected && element.isExpectedOrExpectedClassMember()
val file = element.containingKtFile val file = element.containingKtFile
val project = file.project val project = file.project
@@ -157,8 +157,9 @@ class ConvertMemberToExtensionIntention : SelfTargetingRangeIntention<KtCallable
extension.modifierList?.getModifier(KtTokens.OPEN_KEYWORD)?.delete() extension.modifierList?.getModifier(KtTokens.OPEN_KEYWORD)?.delete()
extension.modifierList?.getModifier(KtTokens.FINAL_KEYWORD)?.delete() extension.modifierList?.getModifier(KtTokens.FINAL_KEYWORD)?.delete()
if (isEffectiveHeader && !extension.hasModifier(KtTokens.HEADER_KEYWORD)) if (isEffectivelyExpected && !extension.hasExpectModifier()) {
extension.addModifier(KtTokens.HEADER_KEYWORD) extension.addModifier(KtTokens.EXPECT_KEYWORD)
}
var bodyToSelect: KtExpression? = null var bodyToSelect: KtExpression? = null
@@ -179,7 +180,7 @@ class ConvertMemberToExtensionIntention : SelfTargetingRangeIntention<KtCallable
when (extension) { when (extension) {
is KtFunction -> { is KtFunction -> {
if (!extension.hasBody() && !isEffectiveHeader) { if (!extension.hasBody() && !isEffectivelyExpected) {
//TODO: methods in PSI for setBody //TODO: methods in PSI for setBody
extension.add(psiFactory.createBlock(bodyText)) extension.add(psiFactory.createBlock(bodyText))
selectBody(extension) selectBody(extension)
@@ -189,7 +190,7 @@ class ConvertMemberToExtensionIntention : SelfTargetingRangeIntention<KtCallable
is KtProperty -> { is KtProperty -> {
val templateProperty = psiFactory.createDeclaration<KtProperty>("var v: Any\nget()=$bodyText\nset(value){\n$bodyText\n}") val templateProperty = psiFactory.createDeclaration<KtProperty>("var v: Any\nget()=$bodyText\nset(value){\n$bodyText\n}")
if (!isEffectiveHeader) { if (!isEffectivelyExpected) {
val templateGetter = templateProperty.getter!! val templateGetter = templateProperty.getter!!
val templateSetter = templateProperty.setter!! val templateSetter = templateProperty.setter!!
@@ -244,13 +245,13 @@ class ConvertMemberToExtensionIntention : SelfTargetingRangeIntention<KtCallable
return extension to bodyToSelect return extension to bodyToSelect
} }
private fun askIfHeaderIsAllowed(file: KtFile): Boolean { private fun askIfExpectedIsAllowed(file: KtFile): Boolean {
if (ApplicationManager.getApplication().isUnitTestMode) { if (ApplicationManager.getApplication().isUnitTestMode) {
return file.allChildren.any { it is PsiComment && it.text.trim() == "// ALLOW_HEADER_WITHOUT_IMPLS" } return file.allChildren.any { it is PsiComment && it.text.trim() == "// ALLOW_HEADER_WITHOUT_IMPLS" }
} }
return Messages.showYesNoDialog( return Messages.showYesNoDialog(
"Do you want to make new extension a header declaration?", "Do you want to make new extension an expected declaration?",
text, text,
Messages.getQuestionIcon() Messages.getQuestionIcon()
) == Messages.YES ) == Messages.YES
@@ -258,18 +259,18 @@ class ConvertMemberToExtensionIntention : SelfTargetingRangeIntention<KtCallable
private fun createExtensionCallableAndPrepareBodyToSelect( private fun createExtensionCallableAndPrepareBodyToSelect(
element: KtCallableDeclaration, element: KtCallableDeclaration,
allowHeader: Boolean = true allowExpected: Boolean = true
): Pair<KtCallableDeclaration, KtExpression?> { ): Pair<KtCallableDeclaration, KtExpression?> {
val headerDeclaration = element.liftToExpected() as? KtCallableDeclaration val expectedDeclaration = element.liftToExpected() as? KtCallableDeclaration
if (headerDeclaration != null) { if (expectedDeclaration != null) {
element.withHeaderImplementations().filterIsInstance<KtCallableDeclaration>().forEach { element.withExpectedActuals().filterIsInstance<KtCallableDeclaration>().forEach {
if (it != element) { if (it != element) {
processSingleDeclaration(it, allowHeader) processSingleDeclaration(it, allowExpected)
} }
} }
} }
return processSingleDeclaration(element, allowHeader) return processSingleDeclaration(element, allowExpected)
} }
private fun newTypeParameterList(member: KtCallableDeclaration): KtTypeParameterList? { private fun newTypeParameterList(member: KtCallableDeclaration): KtTypeParameterList? {
@@ -967,13 +967,13 @@ fun KtNamedDeclaration.isCompanionMemberOf(klass: KtClassOrObject): Boolean {
return containingObject.isCompanion() && containingObject.containingClassOrObject == klass return containingObject.isCompanion() && containingObject.containingClassOrObject == klass
} }
internal fun KtDeclaration.withHeaderImplementations(): List<KtDeclaration> { internal fun KtDeclaration.withExpectedActuals(): List<KtDeclaration> {
val header = liftToExpected() ?: return listOf(this) val header = liftToExpected() ?: return listOf(this)
val implementations = header.actualsForExpected() val implementations = header.actualsForExpected()
return listOf(header) + implementations return listOf(header) + implementations
} }
internal fun KtDeclaration.resolveToHeaderDescriptorIfPossible(): DeclarationDescriptor { internal fun KtDeclaration.resolveToExpectedDescriptorIfPossible(): DeclarationDescriptor {
val descriptor = unsafeResolveToDescriptor() val descriptor = unsafeResolveToDescriptor()
return descriptor.liftToExpected() ?: descriptor return descriptor.liftToExpected() ?: descriptor
} }
@@ -45,7 +45,7 @@ import org.jetbrains.kotlin.idea.highlighter.markers.liftToExpected
import org.jetbrains.kotlin.idea.refactoring.checkSuperMethods import org.jetbrains.kotlin.idea.refactoring.checkSuperMethods
import org.jetbrains.kotlin.idea.refactoring.formatClass import org.jetbrains.kotlin.idea.refactoring.formatClass
import org.jetbrains.kotlin.idea.refactoring.formatFunction import org.jetbrains.kotlin.idea.refactoring.formatFunction
import org.jetbrains.kotlin.idea.refactoring.withHeaderImplementations import org.jetbrains.kotlin.idea.refactoring.withExpectedActuals
import org.jetbrains.kotlin.idea.references.KtReference import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchParameters import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchParameters
@@ -86,7 +86,7 @@ class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() {
fun getSearchInfo(element: PsiElement) = NonCodeUsageSearchInfo(getIgnoranceCondition(), element) fun getSearchInfo(element: PsiElement) = NonCodeUsageSearchInfo(getIgnoranceCondition(), element)
fun searchKotlinDeclarationReferences(declaration: KtDeclaration): Sequence<PsiReference> { fun searchKotlinDeclarationReferences(declaration: KtDeclaration): Sequence<PsiReference> {
val elementsToSearch = if (declaration is KtParameter) declaration.withHeaderImplementations() else listOf(declaration) val elementsToSearch = if (declaration is KtParameter) declaration.withExpectedActuals() else listOf(declaration)
return elementsToSearch.asSequence().flatMap { return elementsToSearch.asSequence().flatMap {
val searchParameters = KotlinReferencesSearchParameters( val searchParameters = KotlinReferencesSearchParameters(
it, it,