diff --git a/ChangeLog.md b/ChangeLog.md index db87996d65a..3b5e0a76bdc 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -3,6 +3,13 @@ +## 1.1-M02 (EAP-2) + +### IDE + +###### New features +- [`KT-12903`](https://youtrack.jetbrains.com/issue/KT-12903) Implement "Inline type alias" refactoring + ## 1.1-M01 (EAP-1) ### Language features diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index fc16cff2510..0fc01886895 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -363,6 +363,7 @@ + diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineTypeAliasHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineTypeAliasHandler.kt new file mode 100644 index 00000000000..cd74271c71b --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineTypeAliasHandler.kt @@ -0,0 +1,199 @@ +/* + * 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.refactoring.inline + +import com.intellij.lang.Language +import com.intellij.lang.refactoring.InlineActionHandler +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.openapi.wm.WindowManager +import com.intellij.psi.PsiElement +import com.intellij.psi.search.searches.ReferencesSearch +import com.intellij.refactoring.HelpID +import com.intellij.refactoring.RefactoringBundle +import com.intellij.refactoring.util.CommonRefactoringUtil +import org.jetbrains.kotlin.builtins.isExtensionFunctionType +import org.jetbrains.kotlin.builtins.isFunctionType +import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor +import org.jetbrains.kotlin.idea.KotlinLanguage +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor +import org.jetbrains.kotlin.idea.core.ShortenReferences +import org.jetbrains.kotlin.idea.core.replaced +import org.jetbrains.kotlin.idea.imports.importableFqName +import org.jetbrains.kotlin.idea.references.KtSimpleNameReference +import org.jetbrains.kotlin.idea.references.mainReference +import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers +import org.jetbrains.kotlin.idea.util.application.executeWriteCommand +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.* +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.types.TypeProjectionImpl +import org.jetbrains.kotlin.types.TypeSubstitutor +import org.jetbrains.kotlin.types.Variance + +class KotlinInlineTypeAliasHandler : InlineActionHandler() { + companion object { + val REFACTORING_NAME = "Inline Type Alias" + } + + private fun showErrorHint(project: Project, editor: Editor?, message: String) { + CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, null) + } + + override fun isEnabledForLanguage(l: Language?) = l == KotlinLanguage.INSTANCE + + override fun canInlineElement(element: PsiElement?) = element is KtTypeAlias + + override fun inlineElement(project: Project, editor: Editor?, element: PsiElement) { + val typeAlias = element as? KtTypeAlias ?: return + val name = typeAlias.name ?: return + val aliasBody = typeAlias.getTypeReference() ?: return + val file = typeAlias.getContainingKtFile() + + val typeAliasDescriptor = typeAlias.resolveToDescriptor() as TypeAliasDescriptor + val typeToInline = typeAliasDescriptor.expandedType + val typeConstructorsToInline = typeAliasDescriptor.typeConstructor.parameters.map { it.typeConstructor } + + val usages = ReferencesSearch.search(typeAlias).mapNotNull { + val refElement = it.element + refElement.getParentOfTypeAndBranch { referenceExpression } + ?: refElement.getNonStrictParentOfType() + } + + if (usages.isEmpty()) return showErrorHint(project, editor, "Type alias '$name' is never used") + + val usagesInOriginalFile = usages.filter { it.containingFile == file } + val isHighlighting = usagesInOriginalFile.isNotEmpty() + highlightElements(project, editor, usagesInOriginalFile) + + if (usagesInOriginalFile.size != usages.size) { + preProcessInternalUsages(aliasBody, usages) + } + + if (!showDialog(project, + name, + REFACTORING_NAME, + typeAlias, + usages, + HelpID.INLINE_VARIABLE)) { + if (isHighlighting) { + val statusBar = WindowManager.getInstance().getStatusBar(project) + statusBar?.info = RefactoringBundle.message("press.escape.to.remove.the.highlighting") + } + return + } + + val psiFactory = KtPsiFactory(project) + + fun inlineIntoType(usage: KtUserType): KtElement? { + val context = usage.analyze(BodyResolveMode.PARTIAL) + + val argumentTypes = usage + .typeArguments + .filterNotNull() + .mapNotNull { + val type = context[BindingContext.ABBREVIATED_TYPE, it.typeReference] ?: + context[BindingContext.TYPE, it.typeReference] + if (type != null) TypeProjectionImpl(type) else null + } + if (argumentTypes.size != typeConstructorsToInline.size) return null + val substitution = (typeConstructorsToInline zip argumentTypes).toMap() + val substitutor = TypeSubstitutor.create(substitution) + val expandedType = substitutor.substitute(typeToInline, Variance.INVARIANT) ?: return null + val expandedTypeText = IdeDescriptorRenderers.SOURCE_CODE.renderType(expandedType) + val needParentheses = + (expandedType.isFunctionType && usage.parent is KtNullableType) || + (expandedType.isExtensionFunctionType && usage.getParentOfTypeAndBranch { receiverTypeReference } != null) + val expandedTypeReference = psiFactory.createType(expandedTypeText) + return usage.replaced(expandedTypeReference.typeElement!!).apply { + if (needParentheses) { + val sample = psiFactory.createParameterList("()") + parent.addBefore(sample.firstChild, this) + parent.addAfter(sample.lastChild, this) + } + } + } + + fun inlineIntoCall(usage: KtReferenceExpression): KtElement? { + val context = usage.analyze(BodyResolveMode.PARTIAL) + + val importDirective = usage.getStrictParentOfType() + if (importDirective != null) { + val reference = usage.getQualifiedElementSelector()?.mainReference + if (reference != null && reference.multiResolve(false).size <= 1) { + importDirective.delete() + } + + return null + } + + val resolvedCall = usage.getResolvedCall(context) ?: return null + val callElement = resolvedCall.call.callElement as? KtCallElement ?: return null + val substitution = resolvedCall.typeArguments + .mapKeys { it.key.typeConstructor } + .mapValues { TypeProjectionImpl(it.value) } + if (substitution.size != typeConstructorsToInline.size) return null + val substitutor = TypeSubstitutor.create(substitution) + val expandedType = substitutor.substitute(typeToInline, Variance.INVARIANT) ?: return null + val expandedTypeFqName = expandedType.constructor.declarationDescriptor?.importableFqName ?: return null + + if (expandedType.arguments.isNotEmpty()) { + val expandedTypeArgumentList = psiFactory.createTypeArguments( + expandedType.arguments.joinToString(prefix = "<", + postfix = ">") { IdeDescriptorRenderers.SOURCE_CODE.renderType(it.type) } + ) + + val originalTypeArgumentList = callElement.typeArgumentList + if (originalTypeArgumentList != null) { + originalTypeArgumentList.replaced(expandedTypeArgumentList) + } + else { + callElement.addAfter(expandedTypeArgumentList, callElement.calleeExpression) + } + } + + val newCallElement = ((usage.mainReference as KtSimpleNameReference).bindToFqName( + expandedTypeFqName, + KtSimpleNameReference.ShorteningMode.NO_SHORTENING + ) as KtExpression).getNonStrictParentOfType() + return newCallElement?.getQualifiedExpressionForSelector() ?: newCallElement + } + + project.executeWriteCommand(RefactoringBundle.message("inline.command", name)) { + val inlinedElements = usages.mapNotNull { + val inlinedElement = when (it) { + is KtUserType -> inlineIntoType(it) + is KtReferenceExpression -> inlineIntoCall(it) + else -> null + } ?: return@mapNotNull null + + postProcessInternalReferences(inlinedElement) + } + + if (inlinedElements.isNotEmpty() && isHighlighting) { + highlightElements(project, editor, inlinedElements) + } + + typeAlias.delete() + + ShortenReferences.DEFAULT.process(inlinedElements) + } + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineValHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineValHandler.kt index 2cc649a3645..5abf2bff458 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineValHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineValHandler.kt @@ -17,15 +17,10 @@ package org.jetbrains.kotlin.idea.refactoring.inline import com.google.common.collect.Sets -import com.intellij.codeInsight.highlighting.HighlightManager import com.intellij.lang.Language import com.intellij.lang.refactoring.InlineActionHandler -import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.editor.Editor -import com.intellij.openapi.editor.colors.EditorColors -import com.intellij.openapi.editor.colors.EditorColorsManager import com.intellij.openapi.project.Project -import com.intellij.openapi.util.Key import com.intellij.openapi.wm.WindowManager import com.intellij.psi.PsiElement import com.intellij.psi.PsiWhiteSpace @@ -33,28 +28,21 @@ import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.refactoring.HelpID import com.intellij.refactoring.RefactoringBundle import com.intellij.refactoring.util.CommonRefactoringUtil -import com.intellij.refactoring.util.RefactoringMessageDialog -import com.intellij.usageView.UsageInfo import com.intellij.util.containers.MultiMap import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.codeInsight.shorten.performDelayedShortening +import org.jetbrains.kotlin.idea.core.ShortenReferences import org.jetbrains.kotlin.idea.core.replaced import org.jetbrains.kotlin.idea.refactoring.addTypeArgumentsIfNeeded import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively import org.jetbrains.kotlin.idea.refactoring.getQualifiedTypeArgumentList -import org.jetbrains.kotlin.idea.refactoring.move.ContainerChangeInfo -import org.jetbrains.kotlin.idea.refactoring.move.ContainerInfo -import org.jetbrains.kotlin.idea.refactoring.move.lazilyProcessInternalReferencesToUpdateOnPackageNameChange -import org.jetbrains.kotlin.idea.refactoring.move.postProcessMoveUsages import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers -import org.jetbrains.kotlin.idea.core.ShortenReferences import org.jetbrains.kotlin.idea.util.application.executeWriteCommand -import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.resolve.BindingContext @@ -67,11 +55,6 @@ import org.jetbrains.kotlin.utils.sure import java.util.* class KotlinInlineValHandler : InlineActionHandler() { - companion object { - private var KtSimpleNameExpression.internalUsageInfos: MutableMap UsageInfo?>? - by CopyableUserDataProperty(Key.create("INTERNAL_USAGE_INFOS")) - } - override fun isEnabledForLanguage(l: Language) = l == KotlinLanguage.INSTANCE override fun canInlineElement(element: PsiElement): Boolean { @@ -162,24 +145,19 @@ class KotlinInlineValHandler : InlineActionHandler() { val referencesInOriginalFile = referenceExpressions.filter { it.containingFile == file } val isHighlighting = referencesInOriginalFile.isNotEmpty() - highlightExpressions(project, editor, referencesInOriginalFile) + highlightElements(project, editor, referencesInOriginalFile) if (referencesInOriginalFile.size != referenceExpressions.size) { - val targetPackages = referenceExpressions.mapNotNullTo(LinkedHashSet()) { (it.containingFile as? KtFile)?.packageFqName } - for (targetPackage in targetPackages) { - if (targetPackage == file.packageFqName) continue - val packageNameInfo = ContainerChangeInfo(ContainerInfo.Package(file.packageFqName), ContainerInfo.Package(targetPackage)) - initializer.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(packageNameInfo) { expr, factory -> - val infos = expr.internalUsageInfos - ?: LinkedHashMap UsageInfo?>() - .apply { expr.internalUsageInfos = this } - infos[targetPackage] = factory - } - } + preProcessInternalUsages(initializer, referenceExpressions) } fun performRefactoring() { - if (!showDialog(project, name, declaration, referenceExpressions)) { + if (!showDialog(project, + name, + RefactoringBundle.message("inline.variable.title"), + declaration, + referenceExpressions, + HelpID.INLINE_VARIABLE)) { if (isHighlighting) { val statusBar = WindowManager.getInstance().getStatusBar(project) statusBar?.info = RefactoringBundle.message("press.escape.to.remove.the.highlighting") @@ -204,15 +182,7 @@ class KotlinInlineValHandler : InlineActionHandler() { doReplace(referenceExpression, initializer) } - .mapNotNull { inlinedExpression -> - val pointer = inlinedExpression.createSmartPointer() - val targetPackage = inlinedExpression.getContainingKtFile().packageFqName - val expressionsToProcess = inlinedExpression.collectDescendantsOfType { it.internalUsageInfos != null } - val internalUsages = expressionsToProcess.mapNotNull { it.internalUsageInfos!![targetPackage]?.invoke(it) } - expressionsToProcess.forEach { it.internalUsageInfos = null } - postProcessMoveUsages(internalUsages) - pointer.element - } + .mapNotNull { postProcessInternalReferences(it) } assignments.forEach { it.delete() } declaration.delete() @@ -225,7 +195,7 @@ class KotlinInlineValHandler : InlineActionHandler() { parametersForFunctionLiteral?.let { addFunctionLiteralParameterTypes(it, inlinedExpressions) } if (isHighlighting) { - highlightExpressions(project, editor, inlinedExpressions) + highlightElements(project, editor, inlinedExpressions) } } performDelayedShortening(project) @@ -254,36 +224,6 @@ class KotlinInlineValHandler : InlineActionHandler() { CommonRefactoringUtil.showErrorHint(project, editor, message, RefactoringBundle.message("inline.variable.title"), HelpID.INLINE_VARIABLE) } - private fun highlightExpressions(project: Project, editor: Editor?, elements: List) { - if (editor == null || ApplicationManager.getApplication().isUnitTestMode) return - - val editorColorsManager = EditorColorsManager.getInstance() - val searchResultsAttributes = editorColorsManager.globalScheme.getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES) - val highlightManager = HighlightManager.getInstance(project) - highlightManager.addOccurrenceHighlights(editor, elements.toTypedArray(), searchResultsAttributes, true, null) - } - - private fun showDialog( - project: Project, - name: String, - property: KtProperty, - referenceExpressions: List - ): Boolean { - if (ApplicationManager.getApplication().isUnitTestMode) return true - - val kind = if (property.isLocal) "local variable" else "property" - val dialog = RefactoringMessageDialog( - RefactoringBundle.message("inline.variable.title"), - "Inline " + kind + " '" + name + "'? " + RefactoringBundle.message("occurences.string", referenceExpressions.size), - HelpID.INLINE_VARIABLE, - "OptionPane.questionIcon", - true, - project - ) - dialog.show() - return dialog.isOK - } - private fun getParametersForFunctionLiteral(initializer: KtExpression): String? { val functionLiteralExpression = initializer.unpackFunctionLiteral(true) ?: return null val context = initializer.analyze(BodyResolveMode.PARTIAL) diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/inlineUtils.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/inlineUtils.kt new file mode 100644 index 00000000000..f1548a6f9af --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/inlineUtils.kt @@ -0,0 +1,102 @@ +/* + * 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.refactoring.inline + +import com.intellij.codeInsight.highlighting.HighlightManager +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.editor.colors.EditorColors +import com.intellij.openapi.editor.colors.EditorColorsManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Key +import com.intellij.psi.PsiElement +import com.intellij.refactoring.RefactoringBundle +import com.intellij.refactoring.util.RefactoringMessageDialog +import com.intellij.usageView.UsageInfo +import org.jetbrains.kotlin.idea.refactoring.move.ContainerChangeInfo +import org.jetbrains.kotlin.idea.refactoring.move.ContainerInfo +import org.jetbrains.kotlin.idea.refactoring.move.lazilyProcessInternalReferencesToUpdateOnPackageNameChange +import org.jetbrains.kotlin.idea.refactoring.move.postProcessMoveUsages +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType +import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer +import java.util.* + +fun highlightElements(project: Project, editor: Editor?, elements: List) { + if (editor == null || ApplicationManager.getApplication().isUnitTestMode) return + + val editorColorsManager = EditorColorsManager.getInstance() + val searchResultsAttributes = editorColorsManager.globalScheme.getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES) + val highlightManager = HighlightManager.getInstance(project) + highlightManager.addOccurrenceHighlights(editor, elements.toTypedArray(), searchResultsAttributes, true, null) +} + +fun showDialog( + project: Project, + name: String, + title: String, + declaration: KtNamedDeclaration, + usages: List, + helpTopic: String? = null +): Boolean { + if (ApplicationManager.getApplication().isUnitTestMode) return true + + val kind = when (declaration) { + is KtProperty -> if (declaration.isLocal) "local variable" else "property" + is KtTypeAlias -> "type alias" + else -> return false + } + val dialog = RefactoringMessageDialog( + title, + "Inline " + kind + " '" + name + "'? " + RefactoringBundle.message("occurences.string", usages.size), + helpTopic, + "OptionPane.questionIcon", + true, + project + ) + dialog.show() + return dialog.isOK +} + +internal var KtSimpleNameExpression.internalUsageInfos: MutableMap UsageInfo?>? + by CopyableUserDataProperty(Key.create("INTERNAL_USAGE_INFOS")) + +internal fun preProcessInternalUsages(element: KtElement, usages: List) { + val mainFile = element.getContainingKtFile() + val targetPackages = usages.mapNotNullTo(LinkedHashSet()) { it.getContainingKtFile().packageFqName } + for (targetPackage in targetPackages) { + if (targetPackage == mainFile.packageFqName) continue + val packageNameInfo = ContainerChangeInfo(ContainerInfo.Package(mainFile.packageFqName), ContainerInfo.Package(targetPackage)) + element.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(packageNameInfo) { expr, factory -> + val infos = + expr.internalUsageInfos + ?: LinkedHashMap UsageInfo?>().apply { expr.internalUsageInfos = this } + infos[targetPackage] = factory + } + } +} + +internal fun postProcessInternalReferences(inlinedElement: E): E? { + val pointer = inlinedElement.createSmartPointer() + val targetPackage = inlinedElement.getContainingKtFile().packageFqName + val expressionsToProcess = inlinedElement.collectDescendantsOfType { it.internalUsageInfos != null } + val internalUsages = expressionsToProcess.mapNotNull { it.internalUsageInfos!![targetPackage]?.invoke(it) } + expressionsToProcess.forEach { it.internalUsageInfos = null } + postProcessMoveUsages(internalUsages) + return pointer.element +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineTypeAlias/extensionFunctionTypeToFunctionType.kt b/idea/testData/refactoring/inline/inlineTypeAlias/extensionFunctionTypeToFunctionType.kt new file mode 100644 index 00000000000..39bd5731b8c --- /dev/null +++ b/idea/testData/refactoring/inline/inlineTypeAlias/extensionFunctionTypeToFunctionType.kt @@ -0,0 +1,7 @@ +class A + +typealias F = A.(A) -> A + +typealias G1 = (F) -> F +typealias G2 = F.() -> F +typealias G3 = F.(F) -> F \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineTypeAlias/extensionFunctionTypeToFunctionType.kt.after b/idea/testData/refactoring/inline/inlineTypeAlias/extensionFunctionTypeToFunctionType.kt.after new file mode 100644 index 00000000000..43fd5f4a172 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineTypeAlias/extensionFunctionTypeToFunctionType.kt.after @@ -0,0 +1,5 @@ +class A + +typealias G1 = (A.(A) -> A) -> A.(A) -> A +typealias G2 = (A.(A) -> A).() -> A.(A) -> A +typealias G3 = (A.(A) -> A).(A.(A) -> A) -> A.(A) -> A \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineTypeAlias/functionTypeToFunctionType.kt b/idea/testData/refactoring/inline/inlineTypeAlias/functionTypeToFunctionType.kt new file mode 100644 index 00000000000..5c8894921f3 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineTypeAlias/functionTypeToFunctionType.kt @@ -0,0 +1,7 @@ +class A + +typealias F = (A) -> A + +typealias G1 = (F) -> F +typealias G2 = F.() -> F +typealias G3 = F.(F) -> F \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineTypeAlias/functionTypeToFunctionType.kt.after b/idea/testData/refactoring/inline/inlineTypeAlias/functionTypeToFunctionType.kt.after new file mode 100644 index 00000000000..6810185949a --- /dev/null +++ b/idea/testData/refactoring/inline/inlineTypeAlias/functionTypeToFunctionType.kt.after @@ -0,0 +1,5 @@ +class A + +typealias G1 = ((A) -> A) -> (A) -> A +typealias G2 = (A) -> A.() -> (A) -> A +typealias G3 = (A) -> A.((A) -> A) -> (A) -> A \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineTypeAlias/functionTypeToNullableType.kt b/idea/testData/refactoring/inline/inlineTypeAlias/functionTypeToNullableType.kt new file mode 100644 index 00000000000..3ef2d7a4759 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineTypeAlias/functionTypeToNullableType.kt @@ -0,0 +1,7 @@ +class A + +typealias F = (A) -> A + +fun foo() { + val f: F? +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineTypeAlias/functionTypeToNullableType.kt.after b/idea/testData/refactoring/inline/inlineTypeAlias/functionTypeToNullableType.kt.after new file mode 100644 index 00000000000..2db5571309b --- /dev/null +++ b/idea/testData/refactoring/inline/inlineTypeAlias/functionTypeToNullableType.kt.after @@ -0,0 +1,5 @@ +class A + +fun foo() { + val f: ((A) -> A)? +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineTypeAlias/keepImports.1.kt b/idea/testData/refactoring/inline/inlineTypeAlias/keepImports.1.kt new file mode 100644 index 00000000000..18881a1a5a6 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineTypeAlias/keepImports.1.kt @@ -0,0 +1,7 @@ +package b + +import a.R + +typealias S = R + +fun foo() = R(1) \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineTypeAlias/keepImports.1.kt.after b/idea/testData/refactoring/inline/inlineTypeAlias/keepImports.1.kt.after new file mode 100644 index 00000000000..88145e973f9 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineTypeAlias/keepImports.1.kt.after @@ -0,0 +1,9 @@ +package b + +import a.A +import a.R +import c.C + +typealias S = A> + +fun foo() = R(1) \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineTypeAlias/keepImports.2.kt b/idea/testData/refactoring/inline/inlineTypeAlias/keepImports.2.kt new file mode 100644 index 00000000000..774eea37762 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineTypeAlias/keepImports.2.kt @@ -0,0 +1,3 @@ +package c + +class C \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineTypeAlias/keepImports.2.kt.after b/idea/testData/refactoring/inline/inlineTypeAlias/keepImports.2.kt.after new file mode 100644 index 00000000000..774eea37762 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineTypeAlias/keepImports.2.kt.after @@ -0,0 +1,3 @@ +package c + +class C \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineTypeAlias/keepImports.kt b/idea/testData/refactoring/inline/inlineTypeAlias/keepImports.kt new file mode 100644 index 00000000000..4f08ce1eabc --- /dev/null +++ b/idea/testData/refactoring/inline/inlineTypeAlias/keepImports.kt @@ -0,0 +1,13 @@ +package a + +import c.C + +class A + +typealias R = A> + +typealias I = R + +fun R(n: Int) { + +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineTypeAlias/keepImports.kt.after b/idea/testData/refactoring/inline/inlineTypeAlias/keepImports.kt.after new file mode 100644 index 00000000000..b806b3ed9f1 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineTypeAlias/keepImports.kt.after @@ -0,0 +1,11 @@ +package a + +import c.C + +class A + +typealias I = A> + +fun R(n: Int) { + +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineTypeAlias/replaceImports.1.kt b/idea/testData/refactoring/inline/inlineTypeAlias/replaceImports.1.kt new file mode 100644 index 00000000000..dcdc38f44c5 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineTypeAlias/replaceImports.1.kt @@ -0,0 +1,5 @@ +package b + +import a.R + +typealias S = R \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineTypeAlias/replaceImports.1.kt.after b/idea/testData/refactoring/inline/inlineTypeAlias/replaceImports.1.kt.after new file mode 100644 index 00000000000..45a5747afb7 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineTypeAlias/replaceImports.1.kt.after @@ -0,0 +1,6 @@ +package b + +import a.A +import c.C + +typealias S = A> \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineTypeAlias/replaceImports.2.kt b/idea/testData/refactoring/inline/inlineTypeAlias/replaceImports.2.kt new file mode 100644 index 00000000000..774eea37762 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineTypeAlias/replaceImports.2.kt @@ -0,0 +1,3 @@ +package c + +class C \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineTypeAlias/replaceImports.2.kt.after b/idea/testData/refactoring/inline/inlineTypeAlias/replaceImports.2.kt.after new file mode 100644 index 00000000000..774eea37762 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineTypeAlias/replaceImports.2.kt.after @@ -0,0 +1,3 @@ +package c + +class C \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineTypeAlias/replaceImports.kt b/idea/testData/refactoring/inline/inlineTypeAlias/replaceImports.kt new file mode 100644 index 00000000000..0fc546d9054 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineTypeAlias/replaceImports.kt @@ -0,0 +1,9 @@ +package a + +import c.C + +class A + +typealias R = A> + +typealias I = R \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineTypeAlias/replaceImports.kt.after b/idea/testData/refactoring/inline/inlineTypeAlias/replaceImports.kt.after new file mode 100644 index 00000000000..29fe6d130f4 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineTypeAlias/replaceImports.kt.after @@ -0,0 +1,7 @@ +package a + +import c.C + +class A + +typealias I = A> \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineTypeAlias/simpleAlias.kt b/idea/testData/refactoring/inline/inlineTypeAlias/simpleAlias.kt new file mode 100644 index 00000000000..f6a6ff49320 --- /dev/null +++ b/idea/testData/refactoring/inline/inlineTypeAlias/simpleAlias.kt @@ -0,0 +1,7 @@ +class A + +typealias X = A + +fun foo() { + val x: X = X() +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/inlineTypeAlias/simpleAlias.kt.after b/idea/testData/refactoring/inline/inlineTypeAlias/simpleAlias.kt.after new file mode 100644 index 00000000000..9d4853b0e3c --- /dev/null +++ b/idea/testData/refactoring/inline/inlineTypeAlias/simpleAlias.kt.after @@ -0,0 +1,5 @@ +class A + +fun foo() { + val x: A = A() +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/AbstractInlineTest.kt b/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/AbstractInlineTest.kt index 70d5b6517c0..f7a3ebdd9ba 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/AbstractInlineTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/AbstractInlineTest.kt @@ -19,6 +19,8 @@ package org.jetbrains.kotlin.idea.refactoring.inline import com.intellij.codeInsight.TargetElementUtil import com.intellij.codeInsight.TargetElementUtil.ELEMENT_NAME_ACCEPTED import com.intellij.codeInsight.TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED +import com.intellij.lang.refactoring.InlineActionHandler +import com.intellij.openapi.extensions.Extensions import com.intellij.openapi.util.io.FileUtil import com.intellij.refactoring.util.CommonRefactoringUtil import com.intellij.testFramework.UsefulTestCase @@ -50,10 +52,9 @@ abstract class AbstractInlineTest : KotlinLightCodeInsightFixtureTestCase() { val afterFileExists = afterFile.exists() val targetElement = TargetElementUtil.findTargetElement(myFixture.editor, ELEMENT_NAME_ACCEPTED or REFERENCED_ELEMENT_ACCEPTED)!! - val handler = KotlinInlineValHandler() - + val handler = Extensions.getExtensions(InlineActionHandler.EP_NAME).firstOrNull { it.canInlineElement(targetElement) } val expectedErrors = InTextDirectivesUtils.findLinesWithPrefixesRemoved(myFixture.file.text, "// ERROR: ") - if (handler.canInlineElement(targetElement)) { + if (handler != null) { try { runWriteAction { handler.inlineElement(myFixture.project, myFixture.editor, targetElement) } diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java index 3f02c7b5765..832c9604391 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java @@ -35,6 +35,51 @@ public class InlineTestGenerated extends AbstractInlineTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/inline"), Pattern.compile("^(\\w+)\\.kt$"), true); } + @TestMetadata("idea/testData/refactoring/inline/inlineTypeAlias") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class InlineTypeAlias extends AbstractInlineTest { + public void testAllFilesPresentInInlineTypeAlias() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/inline/inlineTypeAlias"), Pattern.compile("^(\\w+)\\.kt$"), true); + } + + @TestMetadata("extensionFunctionTypeToFunctionType.kt") + public void testExtensionFunctionTypeToFunctionType() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/inlineTypeAlias/extensionFunctionTypeToFunctionType.kt"); + doTest(fileName); + } + + @TestMetadata("functionTypeToFunctionType.kt") + public void testFunctionTypeToFunctionType() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/inlineTypeAlias/functionTypeToFunctionType.kt"); + doTest(fileName); + } + + @TestMetadata("functionTypeToNullableType.kt") + public void testFunctionTypeToNullableType() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/inlineTypeAlias/functionTypeToNullableType.kt"); + doTest(fileName); + } + + @TestMetadata("keepImports.kt") + public void testKeepImports() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/inlineTypeAlias/keepImports.kt"); + doTest(fileName); + } + + @TestMetadata("replaceImports.kt") + public void testReplaceImports() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/inlineTypeAlias/replaceImports.kt"); + doTest(fileName); + } + + @TestMetadata("simpleAlias.kt") + public void testSimpleAlias() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/inlineTypeAlias/simpleAlias.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/refactoring/inline/inlineVariableOrProperty") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)