-
Notifications
You must be signed in to change notification settings - Fork 5.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Suggested Rename and Change Signature implementation for Kotlin
(cherry picked from commit b9a9000)
- Loading branch information
1 parent
703be29
commit 1c7a952
Showing
12 changed files
with
4,055 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
77 changes: 77 additions & 0 deletions
77
...org/jetbrains/kotlin/idea/refactoring/suggested/KotlinSignaturePresentationBuilder.kt.201
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package org.jetbrains.kotlin.idea.refactoring.suggested | ||
|
||
import com.intellij.refactoring.suggested.SuggestedRefactoringSupport | ||
import com.intellij.refactoring.suggested.SignatureChangePresentationModel.Effect | ||
import com.intellij.refactoring.suggested.SignatureChangePresentationModel.TextFragment.Leaf | ||
import com.intellij.refactoring.suggested.SignaturePresentationBuilder | ||
|
||
internal class KotlinSignaturePresentationBuilder( | ||
signature: SuggestedRefactoringSupport.Signature, | ||
otherSignature: SuggestedRefactoringSupport.Signature, | ||
isOldSignature: Boolean | ||
) : SignaturePresentationBuilder(signature, otherSignature, isOldSignature) | ||
{ | ||
override fun buildPresentation() { | ||
val declarationType = (signature.additionalData as KotlinSignatureAdditionalData).declarationType | ||
val keyword = declarationType.prefixKeyword | ||
if (keyword != null) { | ||
fragments += Leaf("$keyword ") | ||
} | ||
|
||
signature.receiverType?.let { | ||
when (val effect = effect(it, otherSignature.receiverType)) { | ||
Effect.Modified -> { | ||
fragments += Leaf(it, effect) | ||
fragments += Leaf(".") | ||
} | ||
|
||
else -> { | ||
fragments += Leaf("$it.", effect) | ||
} | ||
} | ||
} | ||
|
||
val name = if (declarationType == DeclarationType.SECONDARY_CONSTRUCTOR) "constructor" else signature.name | ||
fragments += Leaf(name, effect(signature.name, otherSignature.name)) | ||
|
||
if (declarationType.isFunction) { | ||
buildParameterList { fragments, parameter, correspondingParameter -> | ||
if (parameter.modifiers.isNotEmpty()) { | ||
fragments += leaf(parameter.modifiers, correspondingParameter?.modifiers ?: parameter.modifiers) | ||
fragments += Leaf(" ") | ||
} | ||
|
||
fragments += leaf(parameter.name, correspondingParameter?.name ?: parameter.name) | ||
|
||
fragments += Leaf(": ") | ||
|
||
fragments += leaf(parameter.type, correspondingParameter?.type ?: parameter.type) | ||
|
||
val defaultValue = parameter.defaultValue | ||
if (defaultValue != null) { | ||
val defaultValueEffect = if (correspondingParameter != null) | ||
effect(defaultValue, correspondingParameter.defaultValue) | ||
else | ||
Effect.None | ||
fragments += Leaf(" = ", defaultValueEffect.takeIf { it != Effect.Modified } ?: Effect.None) | ||
fragments += Leaf(defaultValue, defaultValueEffect) | ||
} | ||
} | ||
} else { | ||
require(signature.parameters.isEmpty()) | ||
} | ||
|
||
signature.type?.let { type -> | ||
when (val effect = effect(type, otherSignature.type)) { | ||
Effect.Added, Effect.Removed, Effect.None -> { | ||
fragments += Leaf(": ${signature.type}", effect) | ||
} | ||
|
||
Effect.Modified -> { | ||
fragments += Leaf(": ") | ||
fragments += Leaf(type, effect) | ||
} | ||
} | ||
} | ||
} | ||
} |
161 changes: 161 additions & 0 deletions
161
...jetbrains/kotlin/idea/refactoring/suggested/KotlinSuggestedRefactoringAvailability.kt.201
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
package org.jetbrains.kotlin.idea.refactoring.suggested | ||
|
||
import com.intellij.psi.PsiNamedElement | ||
import com.intellij.refactoring.suggested.* | ||
import com.intellij.refactoring.suggested.SuggestedRefactoringSupport.Parameter | ||
import com.intellij.refactoring.suggested.SuggestedRefactoringSupport.Signature | ||
import org.jetbrains.kotlin.descriptors.CallableDescriptor | ||
import org.jetbrains.kotlin.idea.caches.project.forcedModuleInfo | ||
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo | ||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny | ||
import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass | ||
import org.jetbrains.kotlin.lexer.KtTokens | ||
import org.jetbrains.kotlin.psi.KtCallableDeclaration | ||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject | ||
import org.jetbrains.kotlin.psi.psiUtil.hasBody | ||
import org.jetbrains.kotlin.renderer.DescriptorRenderer | ||
import org.jetbrains.kotlin.types.KotlinType | ||
import org.jetbrains.kotlin.types.isError | ||
|
||
class KotlinSuggestedRefactoringAvailability(refactoringSupport: SuggestedRefactoringSupport) : | ||
SuggestedRefactoringAvailability(refactoringSupport) | ||
{ | ||
override fun refineSignaturesWithResolve(state: SuggestedRefactoringState): SuggestedRefactoringState { | ||
val newDeclaration = state.declaration as? KtCallableDeclaration ?: return state | ||
val oldDeclaration = state.createRestoredDeclarationCopy(refactoringSupport) as KtCallableDeclaration | ||
oldDeclaration.containingKtFile.forcedModuleInfo = newDeclaration.getModuleInfo() | ||
|
||
val descriptorWithOldSignature = oldDeclaration.resolveToDescriptorIfAny() as CallableDescriptor | ||
val descriptorWithNewSignature = newDeclaration.resolveToDescriptorIfAny() as CallableDescriptor | ||
|
||
val oldSignature = state.oldSignature | ||
val newSignature = state.newSignature | ||
|
||
val (oldReturnType, newReturnType) = refineType( | ||
oldSignature.type, | ||
newSignature.type, | ||
descriptorWithOldSignature.returnType, | ||
descriptorWithNewSignature.returnType | ||
) | ||
|
||
val improvedOldParameterTypesById = mutableMapOf<Any, String>() | ||
val improvedNewParameterTypesById = mutableMapOf<Any, String>() | ||
for (oldParameter in oldSignature.parameters) { | ||
val id = oldParameter.id | ||
val newParameter = newSignature.parameterById(id) ?: continue | ||
val oldIndex = oldSignature.parameterIndex(oldParameter) | ||
val newIndex = newSignature.parameterIndex(newParameter) | ||
val (oldType, newType) = refineType( | ||
oldParameter.type, | ||
newParameter.type, | ||
descriptorWithOldSignature.valueParameters[oldIndex].type, | ||
descriptorWithNewSignature.valueParameters[newIndex].type | ||
) | ||
improvedOldParameterTypesById[id] = oldType!! | ||
improvedNewParameterTypesById[id] = newType!! | ||
} | ||
val oldParameters = oldSignature.parameters.map { it.copy(type = improvedOldParameterTypesById[it.id] ?: it.type) } | ||
val newParameters = newSignature.parameters.map { it.copy(type = improvedNewParameterTypesById[it.id] ?: it.type) } | ||
|
||
val oldAdditionalData = oldSignature.additionalData as KotlinSignatureAdditionalData? | ||
val newAdditionalData = newSignature.additionalData as KotlinSignatureAdditionalData? | ||
val (oldReceiverType, newReceiverType) = refineType( | ||
oldAdditionalData?.receiverType, | ||
newAdditionalData?.receiverType, | ||
descriptorWithOldSignature.extensionReceiverParameter?.type, | ||
descriptorWithNewSignature.extensionReceiverParameter?.type | ||
) | ||
|
||
return state.copy( | ||
oldSignature = Signature.create( | ||
oldSignature.name, | ||
oldReturnType, | ||
oldParameters, | ||
oldAdditionalData?.copy(receiverType = oldReceiverType) | ||
)!!, | ||
newSignature = Signature.create( | ||
newSignature.name, | ||
newReturnType, | ||
newParameters, | ||
newAdditionalData?.copy(receiverType = newReceiverType) | ||
)!! | ||
) | ||
} | ||
|
||
private fun refineType( | ||
oldTypeInCode: String?, | ||
newTypeInCode: String?, | ||
oldTypeResolved: KotlinType?, | ||
newTypeResolved: KotlinType? | ||
): Pair<String?, String?> { | ||
if (oldTypeResolved?.isError == true || newTypeResolved?.isError == true) { | ||
return oldTypeInCode to newTypeInCode | ||
} | ||
|
||
val oldTypeFQ = oldTypeResolved?.fqText() | ||
val newTypeFQ = newTypeResolved?.fqText() | ||
|
||
if (oldTypeInCode != newTypeInCode) { | ||
if (oldTypeFQ == newTypeFQ) { | ||
return newTypeInCode to newTypeInCode | ||
} | ||
} else { | ||
if (oldTypeFQ != newTypeFQ) { | ||
return oldTypeFQ to newTypeFQ | ||
} | ||
} | ||
|
||
return oldTypeInCode to newTypeInCode | ||
} | ||
|
||
private fun KotlinType.fqText() = DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(this) | ||
|
||
override fun detectAvailableRefactoring(state: SuggestedRefactoringState): SuggestedRefactoringData? { | ||
val declaration = state.declaration | ||
if (declaration !is KtCallableDeclaration || KotlinSuggestedRefactoringSupport.isOnlyRenameSupported(declaration)) { | ||
return SuggestedRenameData(declaration as PsiNamedElement, state.oldSignature.name) | ||
} | ||
|
||
val oldSignature = state.oldSignature | ||
val newSignature = state.newSignature | ||
val updateUsagesData = SuggestedChangeSignatureData.create(state, USAGES) | ||
|
||
if (hasParameterAddedRemovedOrReordered(oldSignature, newSignature)) return updateUsagesData | ||
|
||
// for non-virtual function we can add or remove receiver for usages but not change its type | ||
if ((oldSignature.receiverType == null) != (newSignature.receiverType == null)) return updateUsagesData | ||
|
||
val (nameChanges, renameData) = nameChanges(oldSignature, newSignature, declaration, declaration.valueParameters) | ||
|
||
if (hasTypeChanges(oldSignature, newSignature)) { | ||
return if (nameChanges > 0) | ||
updateUsagesData | ||
else | ||
declaration.overridesName()?.let { updateUsagesData.copy(nameOfStuffToUpdate = it) } | ||
} | ||
|
||
return when { | ||
renameData != null -> renameData | ||
nameChanges > 0 -> updateUsagesData | ||
else -> null | ||
} | ||
} | ||
|
||
private fun KtCallableDeclaration.overridesName(): String? { | ||
return when { | ||
hasModifier(KtTokens.ABSTRACT_KEYWORD) -> IMPLEMENTATIONS | ||
hasModifier(KtTokens.OPEN_KEYWORD) -> OVERRIDES | ||
containingClassOrObject?.isInterfaceClass() == true -> if (hasBody()) OVERRIDES else IMPLEMENTATIONS | ||
hasModifier(KtTokens.EXPECT_KEYWORD) -> "actual declarations" | ||
else -> null | ||
} | ||
} | ||
|
||
override fun hasTypeChanges(oldSignature: Signature, newSignature: Signature): Boolean { | ||
return super.hasTypeChanges(oldSignature, newSignature) || oldSignature.receiverType != newSignature.receiverType | ||
} | ||
|
||
override fun hasParameterTypeChanges(oldParam: Parameter, newParam: Parameter): Boolean { | ||
return super.hasParameterTypeChanges(oldParam, newParam) || oldParam.modifiers != newParam.modifiers | ||
} | ||
} |
Oops, something went wrong.