Inline Type Alias
#KT-12903 Fixed
This commit is contained in:
@@ -3,6 +3,13 @@
|
||||
<!-- Find: ([^\`/\[])(KT-\d+) -->
|
||||
<!-- Replace: $1[`$2`](https://youtrack.jetbrains.com/issue/$2) -->
|
||||
|
||||
## 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
|
||||
|
||||
@@ -363,6 +363,7 @@
|
||||
<refactoring.introduceParameterMethodUsagesProcessor
|
||||
implementation="org.jetbrains.kotlin.idea.refactoring.introduce.introduceParameter.KotlinIntroduceParameterMethodUsageProcessor"/>
|
||||
<inlineActionHandler implementation="org.jetbrains.kotlin.idea.refactoring.inline.KotlinInlineValHandler"/>
|
||||
<inlineActionHandler implementation="org.jetbrains.kotlin.idea.refactoring.inline.KotlinInlineTypeAliasHandler"/>
|
||||
<treeStructureProvider implementation="org.jetbrains.kotlin.idea.projectView.KotlinSelectInProjectViewProvider"/>
|
||||
<treeStructureProvider implementation="org.jetbrains.kotlin.idea.projectView.KotlinExpandNodeProjectViewProvider" order="last"/>
|
||||
|
||||
|
||||
+199
@@ -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<KtUserType> { referenceExpression }
|
||||
?: refElement.getNonStrictParentOfType<KtSimpleNameExpression>()
|
||||
}
|
||||
|
||||
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<KtFunctionType> { 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<KtImportDirective>()
|
||||
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<KtCallElement>()
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<FqName, (KtSimpleNameExpression) -> 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<FqName, (KtSimpleNameExpression) -> 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<KtSimpleNameExpression> { 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<PsiElement>) {
|
||||
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<KtExpression>
|
||||
): 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)
|
||||
|
||||
@@ -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<PsiElement>) {
|
||||
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<KtElement>,
|
||||
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<FqName, (KtSimpleNameExpression) -> UsageInfo?>?
|
||||
by CopyableUserDataProperty(Key.create("INTERNAL_USAGE_INFOS"))
|
||||
|
||||
internal fun preProcessInternalUsages(element: KtElement, usages: List<KtElement>) {
|
||||
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<FqName, (KtSimpleNameExpression) -> UsageInfo?>().apply { expr.internalUsageInfos = this }
|
||||
infos[targetPackage] = factory
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun <E : KtElement> postProcessInternalReferences(inlinedElement: E): E? {
|
||||
val pointer = inlinedElement.createSmartPointer()
|
||||
val targetPackage = inlinedElement.getContainingKtFile().packageFqName
|
||||
val expressionsToProcess = inlinedElement.collectDescendantsOfType<KtSimpleNameExpression> { it.internalUsageInfos != null }
|
||||
val internalUsages = expressionsToProcess.mapNotNull { it.internalUsageInfos!![targetPackage]?.invoke(it) }
|
||||
expressionsToProcess.forEach { it.internalUsageInfos = null }
|
||||
postProcessMoveUsages(internalUsages)
|
||||
return pointer.element
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
class A
|
||||
|
||||
typealias <caret>F = A.(A) -> A
|
||||
|
||||
typealias G1 = (F) -> F
|
||||
typealias G2 = F.() -> F
|
||||
typealias G3 = F.(F) -> F
|
||||
Vendored
+5
@@ -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
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
class A
|
||||
|
||||
typealias <caret>F = (A) -> A
|
||||
|
||||
typealias G1 = (F) -> F
|
||||
typealias G2 = F.() -> F
|
||||
typealias G3 = F.(F) -> F
|
||||
+5
@@ -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
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
class A
|
||||
|
||||
typealias <caret>F = (A) -> A
|
||||
|
||||
fun foo() {
|
||||
val f: F?
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
class A
|
||||
|
||||
fun foo() {
|
||||
val f: ((A) -> A)?
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package b
|
||||
|
||||
import a.R
|
||||
|
||||
typealias S = R<Int>
|
||||
|
||||
fun foo() = R(1)
|
||||
@@ -0,0 +1,9 @@
|
||||
package b
|
||||
|
||||
import a.A
|
||||
import a.R
|
||||
import c.C
|
||||
|
||||
typealias S = A<C<Int>>
|
||||
|
||||
fun foo() = R(1)
|
||||
@@ -0,0 +1,3 @@
|
||||
package c
|
||||
|
||||
class C<X>
|
||||
@@ -0,0 +1,3 @@
|
||||
package c
|
||||
|
||||
class C<X>
|
||||
@@ -0,0 +1,13 @@
|
||||
package a
|
||||
|
||||
import c.C
|
||||
|
||||
class A<X>
|
||||
|
||||
typealias <caret>R<X> = A<C<X>>
|
||||
|
||||
typealias I = R<String>
|
||||
|
||||
fun R(n: Int) {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package a
|
||||
|
||||
import c.C
|
||||
|
||||
class A<X>
|
||||
|
||||
typealias I = A<C<String>>
|
||||
|
||||
fun R(n: Int) {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package b
|
||||
|
||||
import a.R
|
||||
|
||||
typealias S = R<Int>
|
||||
@@ -0,0 +1,6 @@
|
||||
package b
|
||||
|
||||
import a.A
|
||||
import c.C
|
||||
|
||||
typealias S = A<C<Int>>
|
||||
@@ -0,0 +1,3 @@
|
||||
package c
|
||||
|
||||
class C<X>
|
||||
@@ -0,0 +1,3 @@
|
||||
package c
|
||||
|
||||
class C<X>
|
||||
@@ -0,0 +1,9 @@
|
||||
package a
|
||||
|
||||
import c.C
|
||||
|
||||
class A<X>
|
||||
|
||||
typealias <caret>R<X> = A<C<X>>
|
||||
|
||||
typealias I = R<String>
|
||||
@@ -0,0 +1,7 @@
|
||||
package a
|
||||
|
||||
import c.C
|
||||
|
||||
class A<X>
|
||||
|
||||
typealias I = A<C<String>>
|
||||
@@ -0,0 +1,7 @@
|
||||
class A
|
||||
|
||||
typealias <caret>X = A
|
||||
|
||||
fun foo() {
|
||||
val x: X = X()
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
class A
|
||||
|
||||
fun foo() {
|
||||
val x: A = A()
|
||||
}
|
||||
@@ -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) }
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user